web-dev-qa-db-ja.com

Java HashMap with Int Array

このコードを使用して、配列が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に存在することを確認するにはどうすればよいですか?

16
Sunil

問題は、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を出力するのはなぜですか。


多くの方法のいずれかで問題を解決できます。

  • equalsJava.util.Arraysequals/deepEqualsメソッドを使用する配列の独自のラッパークラスを定義します。
    • また、@Override equals(Object)の場合は、@Override hashCodeも必要であることを忘れないでください。
  • List<Integer>のようなものを使用してくださいdoes含まれている値の観点からequalsを定義します
  • または、canequalsの参照が等しい場合は、can自分が持っているものに固執するだけです。上記のスニペットがtrueを出力することを期待するべきではないのと同じように、値だけで配列を見つけることができると期待するべきではありません。毎回、元の参照に固執して使用する必要があります。

参照:

API

  • Object.equals および Object.hashCode
    • Javaプログラマーは、これらのコントラクトと、それらをシステムの残りの部分と連携させる方法を知っておくことが不可欠です。
31

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()呼び出しに反映されます。

8
Yuval Adam

私は別のアプローチを使用します。前に述べたように、問題は配列の平等にあります。これは参照の平等に基づいており、マップがニーズに合わなくなります。代わりにArrayListを使用すると仮定した場合の別の潜在的な問題は、一貫性の問題です。マップに追加された後にリストを変更すると、リストの位置にハッシュコードが反映されなくなるため、ハッシュマップが破損します。

これらの2つの問題を解決するために、私はある種の不変のリストを使用します。たとえば、int配列に不変のラッパーを作成し、equals()とhashCode()を自分で実装したい場合があります。

4
Eyal Schneider

問題は、配列が「==」比較を行っていること、つまり参照をチェックしていることだと思います。 containsKey(new int [] {...})を実行すると、新しいオブジェクトが作成されるため、参照は同じではありません。

配列タイプをArrayList<Integer>のようなものに変更すると機能しますが、これはあまり効率的ではないため、リストをマップキーとして使用することは避けがちです。

2
user7094

配列のhashCode()実装はObject.hashCode()から派生しているため、配列のメモリ位置によって異なります。 2つの配列は別々にインスタンス化されるため、メモリの場所が異なり、ハッシュコードも異なります。 1つの配列を作成した場合、それは機能します。

int[] arr = {1, 2};
map.put(arr, "Sun");
System.out.println(map.containsKey(arr));
2
Michael Mrozek

Newを2回呼び出したため、たまたま同じ値を含む2つの異なるオブジェクトがあります。

使用する可能性のあるアプローチの1つは、独自の「ホルダー」クラスを作成し、そのクラスのequalsメソッドとハッシュメソッドを定義することです。

1
djna

Stringsを配列にマップするのではなく、その逆にしたくないですか?

とにかく、あなたの質問に答えるために、問題はあなたがcontainsKey()を呼び出すときにnew配列を作成しているということです。これは、同じ要素と次元を持つ2つの別々のnewed配列がある場合にfalseを返します。配列がキーとして含まれているかどうかを確認する正しい方法については、Yuvalの回答を参照してください。

別のより高度なアプローチは、配列をラップしてhashCode()を上書きする独自のクラスを作成し、同じ次元と要素を持つ2つの配列が等しいハッシュコードを持つようにすることです。

1
Justin Ardini