This question already has answers here:
Java Class that implements Map and keeps insertion order?
(8 answers)
Closed 5 years ago.
I have a HashMap to store my data of <Integer, ArrayList<String>>. I used put(...) to add data in the map if it does not exist yet. I used containsKey() to check if the entry is Key is already in the map. If it exists, I add the String to the key by doing this:
x.get(i).add(str)
To get the data, I just did a for loop with the keySet() of the hash.
If I have this hash:
int -> array of strings
1 -> "a", "b", "c"
2 -> "aa", "bb", "cc"
3 -> "aaa", "bbb", "ccc"
My problem is, the order of the data being read is not consistent.
In 1 PC, it might read the keys in this order: 2, 1, 3. In another PC, it might read it in this order: 1, 2, 3.
I would prefer that the order in which they are read are the same across all PCs. The order into which they are entered into the hashmap is also the same, but Why is the hashmap reading the Keysets in different order?
Did you read the Javadoc of HashMap? It makes no guarantees about ordering.
This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
If you want consistent ordering, use LinkedHashMap or TreeMap. HashMap's lack of guarantees mean that, if the Java developers choose, it can iterate backwards on Tuesdays.
Actually there is no way to control the input order of the elements in Hash Map, but it is possible to sort them before porcess them.
public class HashMapSample {
public static void main(String[] args) {
Map<Integer,List<String>> myHash = new HashMap<>();
myHash.put(1, Arrays.asList("a","aa","aaa"));
myHash.put(3, Arrays.asList("c","ccc","ccc"));
myHash.put(2, Arrays.asList("b","bb","bbb"));
System.out.println(myHash);
myHash.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(System.out::println);
}
}
You need to use TreeMap instead of HashMap
Related
I'm try to sort a hashmap based on its value. For this I'm using a List. Here is my code :
//The original hashmap that needs to be sorted
Map<Subject,Integer> originalMap = new HashMap<>();
originalMap.put(new Subject("English"),1);
originalMap.put(new Subject("French"),5);
originalMap.put(new Subject("Tamil"),6);
originalMap.put(new Subject("Chinese"),3);
//storing the hashmap in a list so that it can be sorted
List<Map.Entry<Subject,Integer>> list = new ArrayList<>(originalMap.entrySet());
list.sort((i,j)->Integer.compare(i.getValue(),j.getValue()));
Map<Subject,Integer> finalMap = new HashMap<>();
for(Map.Entry<Subject,Integer> item:list){
finalMap.put(item.getKey(),item.getValue());
}
finalMap.forEach((i,j)-> System.out.print(j+" "));
I expect the code to print
1 3 5 6
but the output is
6 3 5 1
Also, does the sorting on hashmap makes sense as hashmap do not guarantee the order of elements.
You can not sort a HashMap. This is, because HashMap objects are storing the values unsorted in the memory.
You can test this:
Run the code multiple times and I would bet, that you will become different results (not sure).
Edit:
You can try LinkedHashMap for this.
Objects of this class have a pointer to their next and to their previous object, and because of that, they can be sorted.
Your finalList is a HashMap, order is not guaranteed, use LinkedHashMap in place
This question already has answers here:
How to sort a HashMap in Java [duplicate]
(17 answers)
Closed 6 years ago.
I have a small HashMap (less than 100 entries) that contains a unique object (of my design) as the key and a Double as the value.
I need to retrieve n number of objects that have the lowest values.
So say my HashMap looked like this and I wanted the lowest 3.
Object, 4.0
Object, 5.0
Object, 2.0
Object, 12.0
Object, 10.0
Object, 3.0
I would want to fetch the first, third, and last entries as those have the lowest values.
I know there are methods such as Collections.min which I could run on the HashMap but I need more than just the lowest value and I need to know the key it corresponds to as well. Research has also led me to come across Selection Algorithms but I am confused and not quite sure how to use these. I apologise if a question of this sort has been asked before, I searched for a long time and could not find anything. Thanks pre-emptively for your help.
List<Entry<Key, Double>> lowestThreeEntries = map.entrySet()
.stream()
.sorted(Comparator.comparing(Entry::getValue))
.limit(3)
.collect(Collectors.toList());
Put all then entries of your map inside an array. Then, implement a Comparator<Map.Entry<Key,Value>> which compares the map entries according to the value they hold.
And finally, sort the entries of the map according to your shiny comparator. (You can use Arrays.sort)
Consider inverting your map and using a 'MultiMap' (effectively a more user friendly implementation of Map<Key, List<Value>>), the Guava TreeMultiMap should do exactly what you're after.
http://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/TreeMultimap.html
Edit - Added example (using String in place of the custom object for brevity)
TreeMultimap<Double, String> map = TreeMultimap.<Double, String>create();
map.put(4.0, "a");
map.put(5.0, "b");
map.put(2.0, "c");
map.put(12.0, "d");
map.put(10.0, "e");
map.put(3.0, "f");
Iterator<String> iterator = map.values().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
This will print out the strings in ascending order according to the double value (c, f, a, b, e, d)
According to these:
http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html
Difference between HashMap, LinkedHashMap and TreeMap
java beginner : How key gets sorted in hashmaps?
The HashMap in Java should be unsorted but it is being sorted with respect to Key.
I experienced this as a problem because I needed inserted-order data. So, I used LinkedHashMap instead. But still I am confused why the HashMap sorted it.
Can anyone explain it?
I did a simple example to view the sort.
public static void main(String[] args) {
HashMap<Integer, String> newHashMap = new HashMap<Integer, String>();
newHashMap.put(2, "First");
newHashMap.put(0, "Second");
newHashMap.put(3, "Third");
newHashMap.put(1, "Fourth");
Iterator<Entry<Integer, String>> iterator = newHashMap.entrySet()
.iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
System.out.println("Key: " + entry.getKey());
System.out.println("Value: " + entry.getValue());
iterator.remove();
}
}
Result:
Key: 0
Value: Second
Key: 1
Value: Fourth
Key: 2
Value: First
Key: 3
Value: Third
Edit:
I tried to insert 50 random numbers using Random of Java and I found some data unsorted. But, it still manages to sort most of the integers.
Random results:
...
Key: 36
Value: random
Key: 43
Value: random
Key: 47
Value: random
Key: 44
Value: random
Key: 45
Value: random
...
It's a coincidence (not really, rather it has to do with the hashing algorithm).
Try adding
newHashMap.put(-5, "Fifth");
as last.
Output will be
Key: 0
Value: Second
Key: 1
Value: Fourth
Key: 2
Value: First
Key: 3
Value: Third
Key: -5
Value: Fifth
The javadoc specifically says
This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
You should not infer too much! Just because three or four numbers appear sorted, it doesn't mean they have been sorted.
The hash code of a positive int is usually just that int, so if all your keys are lower than the length of the internal array the Map maintains, they may appear sorted.
Try with really big values, and you'll see that the apparing order vanishes. For example, use
100,200,300,100001, 100002, 10003, 999123456, 888777666, ....
You can't assume that it will be sorted. The reason why it appears sorted, in this simple example: A HashMap is internally constructed from "Bins". These Bins contain the actual elements. They are basically small lists that reside in an array.
[0] -> [ Bin0: ... ]
[1] -> [ Bin1: ... ]
[2] -> [ Bin2: ... ]
[3] -> [ Bin3: ... ]
When an item is inserted into the HashMap, then the "Bin" in which it should be inserted is - to simplify it a little - found by using the hashCode() of the object as an array index. For example, if the hashCode is 2, it will be inserted into Bin 2. When this "index" is greater than the array size, it will be placed into Bin (index%arraySize) - that is, if the hashCode is 5, it will be inserted into Bin 1.
And since a HashMap initially has an internal array size of 10, inserting Integer objects between 0 and 9 will coincidentally place the elements in the right order into the array. (The hashCode of an Integer is just its value, of course).
(Note: The actual algorithms and hash functions may be slightly more complicated, but that's the basic idea)
It's pure coincidence. Sometimes it appears to be sorted, but keep adding keys and the dream will shatter.
I wrote this little program:
import java.util.Map;
import java.util.HashMap;
class MapTest {
public static void main(String[] args){
int count = Integer.parseInt(args[0]);
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < count; i++) map.put(i, i);
System.out.println(map);
}
}
When running java MapTest 20, I get the following output (line-wrapped for readability):
{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10, 11=11, 12=12, 13=13,
14=14, 15=15, 17=17, 16=16, 19=19, 18=18}
It's simply a property of the implementation of HashMap that Integers added sequentially (and starting at 0) at first seem to be ordered.
Like every one is saying (AND is right about) is that you should assume that the keys in an HashMap are not sorted.
Now they LOOK sorted in your case for two simple reasons:
1 - You are using Integer as a key: The HashMap use the hashCode() method of the Object class of Java to find the index in the underlying array it uses to store the Entry instances (what contains your values and keys in the HashMap). It just so happen that the hashcode of an Integer is its own value.
2 - You are not setting the initial size of the HashMap and thus are using its default initial size (which is 16). So until you add a key below 0 or above 16 (included) you will see the keys stored in order. Since the HashMap gets the index by doing
int index = newKey.hashCode() % this.capacity;
Later on HashMap might increase the capacity of its underlying array if you insert a lot key-value pairs (how and when it decides to do that is very interesting if you are into algo and data structure study), so you might end up in a situation in which your Integer keys might look sorted again, but they actually are not intentionally sorted.
Btw if your keys are going to be Integers and you can estimate the maximum key value you are going to have I'd suggest to directly use an array. Access is faster and the memory used will be the same or slightly less.
I would recommend you to use a LinkedHashMap.
In the LinkedHashMap the elements can be accessed in their insertion order.
You can't make assumptions about the orderings on HashMap objects. They will order as they please, implementation-defined. You should treat them as unordered data structures.
Actually It can not be ensured the order.
Hashmap uses hashcode to hash data for search fast.
Your key is so simple, so it sorted.
Mine is an educated guess, but the reason is likely to be the fact that the default hashCode method uses the memory location. The memory location of small Integers (and your keys are autoboxed into Integer) are most likely fixed: it would be nonesense to have Integer.valueOf(1) return a different memory location on multiple calls. Finally, most likely these fixed memory locations are in the ascending order. This would explain this coincidence, but well, one would need to dig into the implementation of Integer and HashMap to prove this.
Correction: in case of Integer "A hash code value for this object, equal to the primitive int value represented by this Integer object." (JavaDoc). Which, although a different number, confirms the idea.
Since no answer really utilized looking at the Java source, I will do so! :)
When you call the put() function, the internal hash function uses the hashCode of the object, to generate the hashing index. [put() source]
The hash() function simply ensures that hashCodes that differ only by constant multiples at each bit position have a bounded number of collisions [use Google to see why that is the case].
Things just coincidentally worked here. That is about it.
Questioner said:
"The HashMap in Java should be unsorted but it is being sorted with respect to Key."
Yes, correct. I will show you with bellow sample
Map<String, String> map1 = new HashMap<String, String>();
map1.put("c","xxxx");
map1.put("b","yyyy");
map1.put("a","zzzz");
for (String key :map1.keySet())
System.out.println(map1.get(key));
System.out.println();
Map<Integer,String> map2 = new HashMap<Integer,String>();
map2.put(3,"xxxx");
map2.put(2,"yyyy");
map2.put(1,"zzzz");
for (int key :map2.keySet())
System.out.println(map2.get(key));
Output shows HashMap use Key to sort data
xxxx
yyyy
zzzz
zzzz
yyyy
xxxx
I have this very simple piece of code, and I was just trying to play a bit with different kind of objects inside a Map.
//There's a bit of spanish, sorry about that
//just think 'persona1' as an object with
//a string and an int
Map mapa = new HashMap();
mapa.put('c', 12850);
mapa.put(38.6, 386540);
mapa.put("Andrés", 238761);
mapa.put(14, "Valor de 14");
mapa.put("p1", persona1);
mapa.put("Andrea", 34500);
System.out.println(mapa.toString());
And then I expect from console something like:
{c=12850, 38.6=386540, Andrés=238761, 14=Valor de 14, p1={nombre: Andres Perea, edad: 10}, Andrea=34500}
But susprisingly for me I got same data in different order:
{38.6=386540, Andrés=238761, c=12850, p1={nombre: Andres Perea, edad: 10}, Andrea=34500, 14=Valor de 14}
It doesn't matter if I try other kind of objects, even just Strings or numeric types, it always does the same, it makes a different without-apparently-any-sense order.
Can someone give me a hint why this happens? Or may be something too obvious I'm missing?
I'm using Java 1.7 and Eclipse Juno.
As per Oracle's documentation
The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls. This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
Refer to HashMap JavaDocs.
There are 3 class which implements map interface in java.
1. hashMap: Id does not guarantee any order.
2. Linked HashMap:It will store them in insertion order.
3. TreeMap: It will store in ascending order.(ASCII value)
So As per your requirement you can use Linked HashMap instead of HashMap.so instead of writing
Map mapa = new HashMap();
create object of Linked HashMap
Map mapa = new LinkedHashMap();
follow below link for more info.
http://docs.oracle.com/javase/tutorial/collections/interfaces/map.html
HashMap not guaranteed the order of element. If you want to keep order use LinkedHashMap.
See following case
Map<Integer,String> unOrderedMap=new HashMap<>();
unOrderedMap.put(1,"a");
unOrderedMap.put(3,"a");
unOrderedMap.put(2,"a");
System.out.println("HashMap output: "+unOrderedMap.toString());
Map<Integer,String> orderedMap=new LinkedHashMap<>();
orderedMap.put(1,"a");
orderedMap.put(3,"a");
orderedMap.put(2,"a");
System.out.println("LinkedHashMap output: "+orderedMap.toString());
Output:
HashMap output: {1=a, 2=a, 3=a}
LinkedHashMap output: {1=a, 3=a, 2=a}
Maps does not maintain the order the order in which elements were added, List will maintain the order of elements
"The order of a map is defined as the order in which the iterators on the map's collection views return their elements. Some map implementations, like the TreeMap class, make specific guarantees as to their order; others, like the HashMap class, do not."
This is how a hashmap works: (citing from another source)
It has a number of "buckets" which it uses to store key-value pairs in. Each bucket has a unique number - that's what identifies the bucket. When you put a key-value pair into the map, the hashmap will look at the hash code of the key, and store the pair in the bucket of which the identifier is the hash code of the key. For example: The hash code of the key is 235 -> the pair is stored in bucket number 235. (Note that one bucket can store more then one key-value pair).
When you lookup a value in the hashmap, by giving it a key, it will first look at the hash code of the key that you gave. The hashmap will then look into the corresponding bucket, and then it will compare the key that you gave with the keys of all pairs in the bucket, by comparing them with equals().
Now you can see how this is very efficient for looking up key-value pairs in a map: by the hash code of the key the hashmap immediately knows in which bucket to look, so that it only has to test against what's in that bucket.
Looking at the above mechanism, you can also see what requirements are necessary on the hashCode() and equals() methods of keys:
If two keys are the same (equals() returns true when you compare them), their hashCode() method must return the same number. If keys violate this, then keys that are equal might be stored in different buckets, and the hashmap would not be able to find key-value pairs (because it's going to look in the same bucket).
If two keys are different, then it doesn't matter if their hash codes are the same or not. They will be stored in the same bucket if their hash codes are the same, and in this case, the hashmap will use equals() to tell them apart.
Now, when you put all your "key-value" pairs in the hashmap, and print them, it prints them in some random order of the keys which got generated by hashing the value you supplied for keys.
If your requirement is still to maintain the ordering, you can use the LinkedHashMap in Java.
Hope this helps :-)
Edit: Original Post: How does a Java HashMap handle different objects with the same hash code?
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Bi-directional Map in Java?
How can I retrieve key of Map by index when keys are not numeric and unordered ?
For example :
Map<String, Integer> test = new TreeMap<String, Integer>();
test.put("a", 1);
test.put("b", 2);
test.put("z", 3);
test.put("m", 4);
I want to get z if I have index 2 or a if I have index 0.
I know I can do dirty loop with increment to get it but is there another smart way to do it ?
What makes this a bit confusing is whether you're referring to the index based on the order the item is added, or based on the natural ordering of the key (eg: alphabetical)
You can obtain the list of may keys using map.keySet() but there's no guarantee the key set will be in the order which you add it in
You can use TreeMap instead of HashMap if you want to keep your data in some kind of ordering (eg: alphabetical ordering). If you prefer other way of ordering you can implement your own comparator