I am writing the below code to sort the hash map values :
private static HashMap sortByValues(HashMap map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return (((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
}
});
}
However When I execute this, it throws an error stating cannot find symbol compareTo. But isnt the method in the String class which is in the lang package?
Also when i replace it by adding a Comparable typecast, it runs fine
private static HashMap sortByValues(HashMap map) {
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
}
});
}
Can someone please help, I am a beginner in Collections.
All of Java's collection classes and interfaces are generics, which means they are intended to be used with type parameters. For historical reasons it's possible to use them without type parameters, which is what you've done here. However, it's a bad idea to do that, because
you sacrifice a lot of the type safety that the compiler gives you,
you end up casting objects to whatever type you need them to be, which is prone to error, and such errors usually only reveal themselves at run time.
Finding errors at compile time is better than finding them at run time. The compiler is your friend - it gives you messages to help you find your errors.
Now in this particular case, you seem to be building a sorted list of values from your map. But it only makes sense to do this if the values in your map belong to some type that can be sorted. There's no general way of sorting Object, so for this to make sense, you want to restrict your parameter to be a map whose values can be sorted, or to put it another way, can be compared to other objects of the same type.
The generic interface that tells you that one object can be compared to another is Comparable. So if you have a type V, then writing V extends Comparable<V> means that one object of type V can be compared to other objects of type V, using the compareTo method. This is the condition that you want the type of the values in your map to obey. You don't need any such condition on the type of the keys in your map.
Therefore, you could write your method as generic, which means that its signature will list some type parameters, inside < > characters, possibly with some conditions on those type parameters. In your case, you'd give your method a signature like this, assuming it's going to return a List.
private static <K, V extends Comparable<V>> List<V> sortAndListValues(Map<K,V> map)
Of course, if you really intend to return some kind of sorted map, then it might be more like
private static <K, V extends Comparable<V>> Map<K,V> sortByValues(Map<K,V> map)
but you need to remember that it's not possible to sort HashMap objects. They're naturally sorted in an order that's implied by the hashCode function of the key class, and by the current size of the map. This is generally not a very useful order. There are other types of map in the JDK, such as
TreeMap, which sorts its entries according to the key - not what you want here
LinkedHashMap, which sorts its entries according to the order they were inserted - and you could probably make use of this here.
For the sake of answering your question though, I'm just going to write the List version of your method.
private static <K, V extends Comparable<V>> List<V> sortAndListValues(Map<K,V> map) {
List<V> toReturn = new LinkedList<>(map.values());
Collections.sort(toReturn, new Comparator<V>() {
public int compare(V first, V second) {
return first.compareTo(second);
}
});
return toReturn;
}
Note that by using the type parameters K and V wherever it's appropriate to do so, there's no need for any kind of casting. The compiler will also warn you if you try to use any of the objects in the map in a way that's inappropriate for their type.
There are shorter ways of writing this of course, using the "functional style" that comes with Java 8. But that's a topic for another post entirely.
#ghostrider - you have removed generics from HashMap so both key and value are of Object type. Inside contents of map are Comparable type but the reference is of Entry<Object, Object> not Entry<Object, Comparable>. Look into the below example.
Object obj = new Integer(5);
int i = obj.intValue(); // Error
int i = ((Integer)obj).intValue(); // Success
Here int i = obj.intValue(); fails but int i = ((Integer)obj).intValue(); get success because i am explicitly type casting because of reference is of Object type.
You can do this by following
private static Map<String, Integer> sortByValue(Map<String, Integer> unsortMap) {
// 1. Convert Map to List of Map
List<Map.Entry<String, Integer>> list =
new LinkedList<Map.Entry<String, Integer>>(unsortMap.entrySet());
// 2. Sort list with Collections.sort(), provide a custom Comparator
// Try switch the o1 o2 position for a different order
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1,
Map.Entry<String, Integer> o2) {
return (o1.getValue()).compareTo(o2.getValue());
}
});
// 3. Loop the sorted list and put it into a new insertion order Map LinkedHashMap
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Map.Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
Related
Basically I'd like something like this: Hashmap<String, String/int> a python equivalent to dictionary in java so be able to store key and value pair, only in my case I need to store the value which can be an int or a string. E.g. of value I'd like to store would be:
{"one":1,
"two":"two"}
So, it's not storing multiple values in one key, just multiple type of value with one type of key. One solution would be to use Hashmap<String, Object> and check the Object instance at runtime, but that really feels tricky and you'd have to check all the values. Is there a more proper way?
There is no another way to do it.
"Everything" in Java extends from Object.
You can create a helper class to handle the checking type or even extend HashMap and create your own getValue method, using generics, like following:
public class MyHashMap extends HashMap<String, Object> {
#Nullable
public <V> V getValue(#Nullable String key) {
//noinspection unchecked
return (V) super.get(key);
}
}
And using like this:
MyHashMap map = new MyHashMap();
map.put("one", 1);
map.put("two", "two");
Integer one = map.getValue("one");
String two = map.getValue("two");
Or even:
public void printNumber(Integer number){
// ...
}
printNumber(map.<Integer>getValue("one"));
So I want to use TreeMap with a customized comparator
My key is a String: id, my value is an int: count;
I NEED TO COMPARE THE COUNT, AS THE VALUE(INTEGER) IN THE TREEMAP
So I have:
In one class:
import java.util.*;
public TreeMap<String, Integer> tm = new TreeMap<String, Integer>(new SortIdCount<Integer>());
In another class:
import java.util.Comparator;
public class SortIdCount implements Comparator<Integer>{
public int compare(Integer count1, Integer count2) {
return count1.compareTo(count2);
}
}
It shows error in eclipse:
The type SortIdCount is not generic; it cannot be parameterized with arguments <Integer>
The type SortIdCount is not generic; it cannot be parameterized with
arguments < Integer >
Reason : Class SortIdCount is not genric type so you can not pass parameterized argument.
Error At Line : (new SortIdCount<Integer>()
Note : A TreeMap is always sorted based on its keys, however if you
want to sort it based on its values then you can build a logic to do
this using comparator. Below is a complete code of sorting a TreeMap
by values.
To Sort on Values You can refer below code snippet.
import java.util.*;
public class TreeMapDemo {
//Method for sorting the TreeMap based on values
public static <K, V extends Comparable<V>> Map<K, V>
sortByValues(final Map<K, V> map) {
Comparator<K> valueComparator =
new Comparator<K>() {
public int compare(K k1, K k2) {
int compare =
map.get(k1).compareTo(map.get(k2));
if (compare == 0)
return 1;
else
return compare;
}
};
Map<K, V> sortedByValues =
new TreeMap<>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
public static void main(String args[]) {
TreeMap<String, Integer> treemap = new TreeMap<>();
// Put elements to the map
treemap.put("Key1", 5);
treemap.put("Key2", 4);
treemap.put("Key3", 3);
treemap.put("Key4", 2);
treemap.put("Key5", 1);
// Calling the method sortByvalues
Map sortedMap = sortByValues(treemap);
// Get a set of the entries on the sorted map
Set set = sortedMap.entrySet();
// Get an iterator
Iterator i = set.iterator();
// Display elements
while(i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
System.out.print(me.getKey() + ": ");
System.out.println(me.getValue());
}
}
}
For more details refer this answer
As others might already have mentioned, that the Comparator used in the TreeMap constructor is used to sort the map by key.
public TreeMap(Comparator comparator)
... ordered according to the given comparator. All keys inserted into the map must be mutually comparable by the given comparator: comparator.compare(k1, k2) must not throw a ClassCastException for any keys k1 and k2 in the map...
But if you want to sort a map by the value, still we have a solution using LinkedHashMap as:
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("hi", 1);
treeMap.put("there", 3);
treeMap.put("hey", 2);
treeMap = treeMap.entrySet().stream().sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldV, newV) -> oldV, LinkedHashMap::new));
System.out.println(treeMap);
Output:
{there=3, hey=2, hi=1}
You can't have a TreeMap which sort by value. There are so many ways to sort a map by value but I believe TreeMap is not for that. If you look on the TreeMap constructor: TreeMap(Comparator<? super K> comparator) which takes comparator as an argument which's generic type clearly should be the super of Key of the Map. So it impossible to send a Comparator<Integer> when the key is String.
Please follow the answer for sorting Map by value.
The comparator used for a TreeMap has to compare the keys, not the values.
So the comparison would have to take the passed key and look up the value in the map. And there you have a classical hen (map) and egg (comparator) problem. You need the comparator to create the map, but since the comparator has to look into the map, you need the map for the comparator. You could solve this by having comparator.setMap(map) after creation of the map.
But the most critical problem is, that your map will not work. As long as the entry is not written to the map, you don't have the value, so the comparator won't work. Only idea I have for that is using two maps. Both String, Integer. The first one is simply used for the lookup in the comparator. So you always have to put into the first and then into the second.
And then you still have problems, because you might easily violate the SortedMap contract. A key must not change (regarding the sorting) after it is inserted into the map. But as soon as you put another value, you have a different sorting of the key.
So I'd really rethink your requirements and whether a TreeMap is the best solution to what you need.
public class SortIdCount implements Comparator<String> {
public int compare(String count1, String count2) {
return count1.compareTo(count2);
}
}
I have a Map<String, AtomicInteger> map in Java that I am trying to sort according to value (and then print). I have found a way that seems quick enough which is to first make an array Object[] a = map.entrySet().toArray();
And then I try to sort it like so
Arrays.sort(a, Comparator.comparingInt((Object entry) -> entry.getValue().get()))
Or like so:
Arrays.sort(a, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Map.Entry<String, AtomicInteger>) o2).getValue()
.compareTo(((Map.Entry<String, AtomicInteger>) o1).getValue());
}
})
Unfortunately both these syntaxes are slightly wrong. In the first case it does not recognise the function getValue(), in the seconds it's compareTo that doesn't work. Can someone suggest corrections to the syntax to make it work?
Here:
Arrays.sort(a, Comparator.comparingInt((Object entry) -> entry.getValue().get()))
You are declaring the parameter to be Object. But it isn't. It is an Entry. And surprise, the class Object doesn't have methods such as getValue()!
And you shouldn't need to give the type in the first place:
Arrays.sort(a, Comparator.comparingInt(e -> entry.getValue().get()))
should do.
And yes, I missed that: the first problem is that a should be an array of Entry values not a Object[].
Besides: use names that mean something. a means like ... nothing.
Why extract into an array? Why not just use sorted() on the entries directly?
For example:
private static List<Map.Entry<String, AtomicInteger>> sortedEntries(
Map<String, AtomicInteger> map) {
return map.entrySet()
.stream()
.sorted(Comparator.comparingInt(entry -> entry.getValue().get()))
.collect(Collectors.toList());
}
I have this question regarding generics.Can anybody explain me why the options 4 and 6[i know about this option]are correct?
Consider the following code:
import java.util.*;
public class TestClass
{
public static void main(String[] args)
{
// put declaration here
m.put("1", new ArrayList()); //1
m.put(1, new Object()); //2
m.put(1.0, "Hello"); //3
System.out.println(m);
}
}
How can 'm' be declared such that the above code will compile and run without errors?
Map m = new TreeMap();
Map<Object, Object> m = new TreeMap<Object, Object>();
Map<Object, ?> map = new LinkedHashMap<Object, Object>();
Map<Object, ? super ArrayList> m = new LinkedHashMap<Object, ArrayList>();will work
if lines //2 and //3 are commented out.
Map<Object, ? super ArrayList> m = new LinkedHashMap<Object, ArrayList>(); will work if lines //1 and //3 are commented out.
Map m = new HashMap();
For understanding this problem, look at the generic signature of the Map#put method you are using here. It states:
V put(K key, V value)
what means that you can put a key that is assignable to the generic type of the Map's key type K and a value that is assignable to the generic type of the Map's value type V. This must be true for all your key-value pairs you put into the map. From your code, you are putting the following keys into the map:
A String by the literal "1"
An Integer by the boxed int literal 1.
A Double by the boxed double literal 1.0.
The only common super type of these objects is the Object type which is required for K in order to allow all these objects to be used as a key.
For the values you have:
A ArrayList instance.
An Object instance
A String by the literal "Hello"
Again, the only common super type of these objects is the Object type which is required for V in order to allow all these objects to be used as a map value.
As a result, only Map instances with the generic signature Map<Object, Object> are permitted. What implementation of the Map interface you choose is up to you, as long as it is assignable to the variable type of the map, i.e. you can use a LinkedHashMap, a TreeMap or a HashMap since they only differ in the way they store their data. For the generic type of the variables, note that the use of wildcards ? or ? extends ... for your variable will result in you not being able to put values into the map anymore. The tutorial I linked explains why.
As for the types with a non-generic signature you are using, they behave similar to Maps with the generic signature <Object, Object>. Such raw types should however not longer be used after the introduction of Java 5.
With all this information, you can answer your (exam) question yourself.
Number 4 is correct for line 1, because "1" is String which has Object superclass and ? super ArrayList means that you can use ArrayList or any superclass of ArrayList.
Number 6 is correct because you are using untyped(raw) map, so it's similar to:
Map<Object, Object> m = new HashMap<Object, Object>();
To store such values you can use Map, but it's not a really good choice. You shouldn't use untyped collections at all. Think how you can change your design to not use such map.
I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.