2009-12-10 11 views
7

オブジェクトのコレクションを1行1オブジェクト= 1行で表示するアプリケーションがあります。オブジェクトはHashMapに格納されます。行の順序はアプリケーションの機能に影響しません(ソート可能なコレクションの代わりにHashMapが使用された理由)。同じプログラムをJVM5とJVM6で実行すると、HashMap内の項目の順序が異なります

しかし、2つの異なるバージョンのJava Virtual Machineを使用して実行した場合、同じアプリケーションが異なる動作をすることに気付きました。アプリケーションはJDK 5を使用してコンパイルされ、Java 5またはJava 6のランタイムを使用して機能的な違いなく実行できます。

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

なぜ、Javaランタイムの選択が注文に影響しますか?

+2

( 'LinkedHashMap'は一貫した順序を与えることができます) –

答えて

17

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; 
} 
+2

+1です。 Michael、私はJDK 5から同等のコードを追加してその点を説明しました。あなたの答えに適切ではないと感じたら編集を元に戻してください。 –

+0

+1 ...私はAndrzejにも投票できますか? :) – skaffman

+0

いいえ、それはまさに私が掘り起こすにはあまりにも怠惰で、手元には1.5 JDKを持っていませんでした。 –

10

おそらくMapに特定の反復順序があると定義されていないためです。要素が戻ってくる順序は、内部実装の成果物となる可能性が高く、一貫性を保つ必要はありません。

(特にパフォーマンス上の理由から)Java 5と6の間で実装が更新された場合、繰り返しの順序が両者で一貫していることを確認するメリットはありません。

EDIT:私はちょうど(残念ながら私は正確なバージョンのわからないんだけど、それは明らかに2006年6月からHashMapの1.68です)早期のJava 6のリリースの一つに興味深いスニペットを見つけました:

/** 
    * 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は実際に反復秩序の一貫性を考慮しました。後で、このコードはおそらく削除され、新しい順序が決定的なものになりました。

+0

うん、私はそれを知っていた。注文が私にとって重要なものだった場合、私は注文が保存されている別のタイプのコレクションを選択したでしょう。なぜか、私はちょっと不思議でした。 +1の編集は@Michael Borgwardtの答え – bguiz

0

HashMapのは、特定の結婚ではありません順序が、LinkedHashMapマップの実装は、順序を保持する必要があります。

関連する問題