当在JVM5和JVM6中运行相同的程序时,HashMap中的项目顺序会有所不同

我有一个应用程序,它显示行中的对象集合,一个对象=一行。 对象存储在HashMap中。 行的顺序不会影响应用程序的function(这就是使用HashMap而不是可排序集合的原因)。

但是我注意到,使用两个不同版本的Java虚拟机运行时,相同的应用程序运行方式不同。 该应用程序使用JDK 5编译,可以使用Java 5或Java 6运行时运行,没有任何function差异。

有问题的对象会覆盖java.lang.Object#hashCode()并且显然已经注意遵循Java API中指定的契约。 这可以通过以下事实得到certificate:它们在应用程序的每次运行中(在同一Java运行时中)始终以相同的顺序出现。

出于好奇,为什么Java运行时的选择会影响订单?

HashMap的实现细节可以并且确实会发生变化。 这个包私有方法最有可能(这是来自JDK 1.6.0_16):

 /** * Applies a supplemental hash function to a given hashCode, which * defends against poor quality hash functions. This is critical * because HashMap uses power-of-two length hash tables, that * otherwise encounter collisions for hashCodes that do not differ * in lower bits. Note: Null keys always map to hash 0, thus index 0. */ static int hash(int h) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } 

作为参考,JDK 1.5.0_06中的模拟是:

 /** * Returns a hash value for the specified object. In addition to * the object's own hashCode, this method applies a "supplemental * hash function," which defends against poor quality hash functions. * This is critical because HashMap uses power-of two length * hash tables.

* * The shift distances in this function were chosen as the result * of an automated search over the entire four-dimensional search space. */ static int hash(Object x) { int h = x.hashCode(); h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h; }

可能是因为Map没有定义为具有任何特定的迭代顺序; 元素返回的顺序可能是其内部实现的工件,并且不需要保持一致。

如果在Java 5和6之间更新实现(特别是出于性能原因),Sun没有任何好处或义务确保迭代顺序在两者之间保持一致。

编辑 :我刚刚在Java 6早期版本中发现了一个有趣的片段(遗憾的是我不确定它的确切版本,但显然是2006年6月的HashMap 1.68):

  /** * Whether to prefer the old supplemental hash function, for * compatibility with broken applications that rely on the * internal hashing order. * * Set to true only by hotspot when invoked via * -XX:+UseNewHashFunction or -XX:+AggressiveOpts */ private static final boolean useNewHash; static { useNewHash = false; } private static int oldHash(int h) { h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h; } private static int newHash(int h) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } 

所以看起来尽管我上面的断言,Sun确实考虑了迭代顺序的一致性 – 在稍后的某个时候,这个代码可能会被删除,而新的顺序也是最终的。

HashMap没有与任何特定的排序结合,但是Map的LinkedHashMap实现应该保留顺序。