7

オブジェクトのコレクションを行で表示するアプリケーションがあります。1 つのオブジェクト = 1 つの行です。オブジェクトは HashMap に格納されます。行の順序は、アプリケーションの機能には影響しません (そのため、ソート可能なコレクションの代わりに HashMap が使用されました)。

しかし、Java 仮想マシンの 2 つの異なるバージョンを使用して実行すると、同じアプリケーションが異なる方法で実行されることに気付きました。このアプリケーションは JDK 5 を使用してコンパイルされており、Java 5 または Java 6 ランタイムのいずれかを使用して実行でき、機能上の違いはありません。

問題のオブジェクトはオーバーライドjava.lang.Object#hashCode()され、明らかに Java API で指定された契約に従うように注意が払われています。これは、(同じ Java ランタイムで) アプリケーションを実行するたびに常に同じ順序で表示されるという事実によって証明されます。

念のために言っておきますが、Java ランタイムの選択が順序に影響するのはなぜですか?

4

3 に答える 3

17

HashMapcan と doの実装の詳細が変更されました。ほとんどの場合、このパッケージのプライベート メソッドが実行されました (これは 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;
}
于 2009-12-10T09:54:03.113 に答える
10

おそらく、 aMapが特定の反復順序を持つように定義されていないためです。要素が戻ってくる順序は、内部実装のアーティファクトである可能性が高く、一貫性を保つ必要はありません。

実装が Java 5 と 6 の間で (特にパフォーマンス上の理由で) 更新された場合、2 つの間で反復順序が一貫していることを確認する Sun の利益も義務もありません。

編集: 初期の Java 6 リリースの 1 つで興味深いスニペットを見つけました (残念ながら正確なバージョンはわかりませんが、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 は実際には反復順序の一貫性を考慮していたようです。後の時点で、このコードはおそらく削除され、新しい順序が決定的なものになりました。

于 2009-12-10T09:52:54.990 に答える
0

HashMap は特定の順序とは関係ありませんが、Map のLinkedHashMap実装では順序を維持する必要があります。

于 2009-12-10T15:03:48.303 に答える