web-dev-qa-db-ja.com

セットに配列を格納し、重複を回避する

HashSet<String[]> boog = new HashSet<String[]>();
boog.add(new String[]{"a", "b", "c"});
boog.add(new String[]{"a", "b", "c"});
boog.add(new String[]{"a", "b", "d"});

結果は

[a, b, c]
[a, b, d]
[a, b, c]

どこ [a,b,c]が繰り返されているため、ハッシュ関数が期待どおりに機能していません。文字列配列のハッシュメソッドをオーバーライドするにはどうすればよいですか。それとも、一般的な配列ですか?私がやろうとしていることを達成するためのより良い方法はありますか?

27
Booker

できません。配列は、デフォルトのIDベースのObject.hashCode()実装を使用します。これをオーバーライドする方法はありません。 HashMap/HashSetで配列をキーとして使用しないでください。

代わりにリストのセットを使用してください。

33

「より良い方法」は、コレクションを使用することです。 String[]の代わりにListを使用してください:

Set<List<String>> boog = //...
boog.add(Arrays.asList("a", "b", "c"));
boog.add(Arrays.asList("a", "b", "c"));
boog.add(Arrays.asList("a", "b", "d"));

System.out.println(boog.size()); // 2

編集する

配列をキーとして使用する必要がある場合は、各キーの周囲に透明なラッパーを作成して、マップに配置できます。いくつかのライブラリはそれを手助けします。たとえば、 Trove を使用してSet<String[]>を実行する方法は次のとおりです。

Set<String[]> boog = new TCustomHashSet<String[]>(new ArrayHashingStrategy());

boog.add(new String[]{"a", "b", "c"});
boog.add(new String[]{"a", "b", "c"});
boog.add(new String[]{"a", "b", "d"});

System.out.println(boog.size()); // 2

//...
public class ArrayHashingStrategy extends HashingStrategy<Object[]> {

   public int computeHashCode(Object[] array) {
      return Arrays.hashCode(array);
   }

   public boolean equals(Object[] arr1, Object[] arr2) {
      return Arrays.equals(arr1, arr2);
   }
}        
26
Mark Peters

配列のhashCode()はデフォルトの実装を使用しますが、これは要素を考慮に入れていないため、変更することはできません。

代わりに、Listを使用できます。hashCode()は、その要素のハッシュコードに基づいて計算されます。 ArrayList(ほとんどの実装として)は、このような関数を使用します。


別の方法(ただし、配列の使用を強制されない限り、あまり好ましくない)では、key.hashCode()を呼び出す代わりに、「特別な」HashSetを使用できます。Arrays.hashCode(array)を呼び出します。これを実装するには、HashMapを拡張してからCollections.newSetFromMap(map)を使用します

4
Bozho

実際には、すべての異なる配列に対して異なる値を返すデフォルトのhashCodeメソッドを使用しています!

これを解決する最良の方法は、CollectionListSetなど)を使用するか、次のような独自のラッパークラスを定義することです。

public class StringArray {
    public String[] stringArray;

    [...] // constructors and methods

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        for(String string : stringArray){
            result = prime * result + ((string == null) ? 0 : string.hashCode());
        }
    }
}

このクラスは実際にはhashCodeのメソッドとほとんど同じListメソッドを使用します。

あなたは今扱う:

HashSet<StringArray> boog = new HashSet<StringArray>();
1
Jean Logeart

実際にできます。 TreeSetは、提供されたComparatorとともに使用できます。あなたの場合、それは次のようなものになります:

Set<String[]> boog = new TreeSet<>((o1, o2) -> {
    for (int i = 0; i < o1.length; i++){
        int cmp = o1[i].compareTo(o2[i]);
        if (cmp != 0) {
            return cmp;
        }
    }
    return o1.length - o2.length;
});

フードの下では、アルファベット順にソートされたツリーのように見えます。

0
Denis Makarskiy