【问题标题】:Order of items in a HashMap differ when the same program is run in JVM5 vs JVM6当在 JVM5 和 JVM6 中运行相同的程序时,HashMap 中的项目顺序不同
【发布时间】:2014-09-17 19:46:48
【问题描述】:

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

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

有问题的对象覆盖了java.lang.Object#hashCode(),显然已经注意遵守Java API 中指定的约定。事实证明,它们在应用程序的每次运行中总是以相同的顺序出现(在同一个 Java 运行时中)。

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

【问题讨论】:

  • LinkedHashMap可以给你一致的顺序。)

标签: java hashmap hashcode


【解决方案1】:

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.<p>
 *
 * 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;
}

【讨论】:

  • +1; Michael,我添加了 JDK 5 中的等效代码来说明这一点;如果您认为答案不合适,请还原我的编辑。
  • 不,这正是我懒得挖掘的原因,手头没有 1.5 JDK。
  • java 7 和 java 8 一样
【解决方案2】:

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

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

编辑:我刚刚在 Java 6 的早期版本之一中发现了一个有趣的 sn-p(不幸的是,我不确定确切的版本,但它显然是 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 实际上确实考虑了迭代顺序的一致性——在稍后的某个时间点,这段代码可能被删除了,新的顺序成为了最终的顺序。

【讨论】:

  • 是的,我知道。如果订单对我来说很重要,我会选择另一种类型的收藏,其中保留了订单。我只是好奇为什么。 +1 编辑@Michael Borgwardt 的答案
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 1970-01-01
    • 2017-09-02
    • 1970-01-01
    • 2019-06-25
    相关资源
    最近更新 更多