このコードを使用して、配列がHashMap
に存在することを確認しています。
public class Test {
public static void main(String[] arg) {
HashMap<int[], String> map = new HashMap<int[], String>();
map.put(new int[]{1, 2}, "Sun");
System.out.println(map.containsKey((new int[]{1, 2})));
}
}
しかし、これはFalse
を出力します。配列がHashMap
に存在することを確認するにはどうすればよいですか?
問題は、2つのint[]
が等しくないためです。
System.out.println(
(new int[] { 1, 2 }).equals(new int[] { 1, 2 })
); // prints "false"
Map
およびその他のJavaコレクションフレームワーククラスは、equals
の観点からインターフェイスを定義します。From Map API
:
Collections Frameworkインターフェースの多くのメソッドは、
equals
メソッドで定義されています。たとえば、containsKey(Object key)
メソッドの仕様には、「このマップにキーtrue
のマッピングが(key==null ? k==null : key.equals(k))
のように含まれている場合にのみ、k
が返されます」と記載されています。
それらは同じオブジェクトである必要はないことに注意してください。それらはequals
である必要があります。 Javaの配列はObject
から拡張され、equals
のデフォルトの実装はオブジェクトIDに対してのみtrueを返します。したがって、上記のスニペットにfalse
を出力するのはなぜですか。
多くの方法のいずれかで問題を解決できます。
equals
が Java.util.Arrays
equals/deepEquals
メソッドを使用する配列の独自のラッパークラスを定義します。@Override equals(Object)
の場合は、@Override hashCode
も必要であることを忘れないでください。List<Integer>
のようなものを使用してくださいdoes含まれている値の観点からequals
を定義しますequals
の参照が等しい場合は、can自分が持っているものに固執するだけです。上記のスニペットがtrue
を出力することを期待するべきではないのと同じように、値だけで配列を見つけることができると期待するべきではありません。毎回、元の参照に固執して使用する必要があります。Object.equals
および Object.hashCode
2つの異なる参照を比較しています。このようなものが機能します:
_public class Test {
public static void main(String[] arg)
{
HashMap<int[],String> map= new HashMap<int[],String>();
int[] a = new int[]{1,2};
map.put(a, "Sun");
System.out.println(map.containsKey(a));
}
}
_
a
は同じ参照であるため、期待どおりにtrue
を受け取ります。アプリケーションに比較を行うための参照を渡すオプションがない場合は、_int[]
_を含む新しいオブジェクトタイプを作成し、equals()
メソッドをオーバーライドします(hashCode()
同時に)、それはcontainsKey()
呼び出しに反映されます。
私は別のアプローチを使用します。前に述べたように、問題は配列の平等にあります。これは参照の平等に基づいており、マップがニーズに合わなくなります。代わりにArrayListを使用すると仮定した場合の別の潜在的な問題は、一貫性の問題です。マップに追加された後にリストを変更すると、リストの位置にハッシュコードが反映されなくなるため、ハッシュマップが破損します。
これらの2つの問題を解決するために、私はある種の不変のリストを使用します。たとえば、int配列に不変のラッパーを作成し、equals()とhashCode()を自分で実装したい場合があります。
問題は、配列が「==」比較を行っていること、つまり参照をチェックしていることだと思います。 containsKey(new int [] {...})を実行すると、新しいオブジェクトが作成されるため、参照は同じではありません。
配列タイプをArrayList<Integer>
のようなものに変更すると機能しますが、これはあまり効率的ではないため、リストをマップキーとして使用することは避けがちです。
配列のhashCode()
実装はObject.hashCode()
から派生しているため、配列のメモリ位置によって異なります。 2つの配列は別々にインスタンス化されるため、メモリの場所が異なり、ハッシュコードも異なります。 1つの配列を作成した場合、それは機能します。
int[] arr = {1, 2};
map.put(arr, "Sun");
System.out.println(map.containsKey(arr));
Newを2回呼び出したため、たまたま同じ値を含む2つの異なるオブジェクトがあります。
使用する可能性のあるアプローチの1つは、独自の「ホルダー」クラスを作成し、そのクラスのequalsメソッドとハッシュメソッドを定義することです。
Strings
を配列にマップするのではなく、その逆にしたくないですか?
とにかく、あなたの質問に答えるために、問題はあなたがcontainsKey()
を呼び出すときにnew
配列を作成しているということです。これは、同じ要素と次元を持つ2つの別々のnew
ed配列がある場合にfalseを返します。配列がキーとして含まれているかどうかを確認する正しい方法については、Yuvalの回答を参照してください。
別のより高度なアプローチは、配列をラップしてhashCode()
を上書きする独自のクラスを作成し、同じ次元と要素を持つ2つの配列が等しいハッシュコードを持つようにすることです。