It would appear at first glance from your example code that HashMap's output ordering of a given set of Strings will always be the same depending on input order because:
- HashMap uses
hashCode
- hashcode on a string is consistent for any given String †
However, the Java HashMap in it's current form may change the ordering as more elements are inserted (changing the hash table size), or for different insertion order of the same String set. For example:
for (int i = 1; i < 15; i++) {
//for (int i = 14; i > 0; i--) {
map1.put(String.format("%04d", i), "");
System.out.println(String.format("%04d:", i) + map1);
}
Running that forwards and backwards yields different iteration ordering after 14 insertions. Also between inserting "0013" and "0014" (running forwards) the last few elements are the same but the iteration order is changed:
0013:{... 0012=, 0003=, 0011=, 0002=, 0010=, 0009=, 0008=}
0014:{... 0003=, 0012=, 0002=, 0011=, 0009=, 0008=, 0010=}
This may look random, but run it again and it will happen in exactly the same way. Therefore this particular implementation is unpredictable in it's ordering as elements are inserted, but deterministic given the same starting and input conditions. I emphasise implementation as, in J7 (u6+), you can change this behaviour for various hash-based collections using java -Djdk.map.althashing.threshold=<threshold> such that, across different JVM instances on the same machine, this behaviour becomes unpredictable.
Consistent hash-based map ordering
LinkedHashMap will maintain iteration order (normally insertion-order). If you want to play around with the differences between the two, you can see the results more clearly with non value-based hashCodes. You could wrap the String as so:
class StrCont {
private String s;
public StrCont (String s) { this.s = s; }
public String toString() { return this.s; }
// uses the Object.hashCode implementation
}
The StrCont class uses the default hashCode from Object. Therefore it is (generally) a hexString of the memory location for the object; the wrapped String becomes irrelevant to the hash. Using that as your key:
map1.put( new StrCont("E._AUTO"), "20");
map1.put( new StrCont("E._ITERATIVE"), "20");
map1.put( new StrCont("E._ANDREW"), "20");
// need only 5/6 more than this to highlight the differences
Repeating this more than once produces new object references with the same String "value", but totally different hashCodes. Ordering is completely destroyed for the HashMap, and maintained for the LinkedHashMap.
TLDR: Value-based hashCodes (such as those from String) in your current JRE's HashMap implementation are a distracting case where, because of your chosen implementation's (also internal state-based) determinism, you may start to think all HashMaps give a consistent ordering based on the hashCode.
But if you depend on consistent iteration ordering you need to use an ordered hash map such as LinkedHashMap.
† While this is true (in J7 only) from J7u6 there is a hash32 function which maps may use if they have been switched to use the alternative hashing method with java -Djdk.map.althashing.threshold=<minEntries>. This can therefore produce different ordering for the same String key input sequences even between restarts of a given JVM on the same machine.