Reputation: 2366
Is it guaranteed that keys and values in standard implementations of java.util.Map
are returned in the same order? For example, if map contains mapping x1 -> y1
and x2 -> y2
, then if keySet()
iteration yields x1, x2
, is it guaranteed that values()
iteration will yield y1, y2
and not y2, y1
? I haven't seen anywhere guaranteed that this is true, but it seems to work. Could anyone give confirm or deny this premise and give counterexample?
public class MapsTest {
@Test
public void hashMapKeysAndValuesAreInSameOrder() {
assertKeysAndValuesAreInSameOrder(new HashMap<>());
}
@Test
public void treeMapKeysAndValuesAreInSameOrder() {
assertKeysAndValuesAreInSameOrder(new TreeMap<>());
}
private void assertKeysAndValuesAreInSameOrder(Map<Integer, Integer> map) {
Random random = new Random();
IntStream.range(0, 100000).map(i -> random.nextInt()).forEach(i -> map.put(i, i));
assertEquals(new ArrayList<>(map.keySet()), new ArrayList<>(map.values()));
}
}
Upvotes: 4
Views: 1594
Reputation: 17874
Looking at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java#HashMap.values%28%29, I'd guess it works because both the KeyIterator and ValueIterator extend HashIterator.
But that's only if you don't change the HashMap in the mean time, e.g. adding and removing items between calling keySet() and values(), because that can change the bucket size, and the HashIterator will behave differently.
Upvotes: 2
Reputation: 4464
Interesting question. I gave it a shot. and it seems the RELATIVE ordering of the keys remains in sync with the values.
import java.util.*;
public class MapClass {
public static Map<String, Integer> map = new HashMap<>();
public static void main (String[] args){
map.put("first", 1);
map.put("second", 2);
map.put("third", 3);
map.put("fourth", 4);
map.put("fifth", 5);
System.out.println(map.keySet());
System.out.println(map.values());
map.put("sixth", 6);
System.out.println(map.keySet());
System.out.println(map.values());
map.remove("first");
System.out.println(map.keySet());
System.out.println(map.values());
}
}
I get:
[third, fifth, fourth, first, second]
[3, 5, 4, 1, 2]
[sixth, third, fifth, fourth, first, second]
[6, 3, 5, 4, 1, 2]
[sixth, third, fifth, fourth, second]
[6, 3, 5, 4, 2]
This simple example seems to show that while a HashMap
will not preserve the order of insertion, it seems it does preserve the relative ordering of keys and values.
keyset() and values() does not guarantee any order though, but it seems in practice that it'll return the same order for multiple invocations. As the comment on there says, even if you could rely on it, it's questionable whether you should.
Upvotes: 4
Reputation: 2033
From the doc of HashMap
,
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 look at its code, you will find out that once you call put()
, eventually hash
is calculated, entry object
is created and added into a bucket array
. I have over simplified the purpose of the code just to explain what happens behind the scene.
If you want guaranteed order, use LinkedHashMap
or TreeMap
depending on your requirements
Also check,
Difference between HashMap, LinkedHashMap and TreeMap
Upvotes: 6
Reputation: 288
From the HashMap docs:
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.
Upvotes: 3