开发者

Storing arrays in Set and avoiding duplicates

开发者 https://www.devze.com 2023-04-06 17:46 出处:网络
HashSet<String[]> boog = new HashSet<String[]>(); boog.add(new String[]{\"a\", \"b\", \"c\"});
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"开发者_JAVA百科, "b", "d"});

results in

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

where [a,b,c] is repeated, so the hash function is not working as expected. How would I go about overriding the Hash method for String arrays. Or for that matter, a generic array? Is there a better way to accomplish what I'm trying to do?


You can't. arrays use the default identity-based Object.hashCode() implementation and there's no way you can override that. Don't use Arrays as keys in a HashMap / HashSet!

Use a Set of Lists instead.


The "better way" is to use collections. Use a List instead of a String[]:

// TreeSet and other comparison based sets will not work as ArrayList does not implement Comparable
Set<List<String>> boog = new HashSet<>();
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

Edit

If you absolutely needed to use arrays as keys, you could build a transparent wrapper around each key and put that in the map. Some libraries help you with that. For example, here's how you could do a Set<String[]> using Trove:

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);
   }
}        


hashCode() of arrays uses the default implementation, which doesn't take into account the elements, and you can't change that.

You can use a List instead, with a hashCode() calculated based on the hashcodes of its elements. ArrayList (as most implementations) uses such function.


Alternatively (but less preferable, unless you are forced somehow to use arrays), you can use a 'special' HashSet where instead of invoking key.hashCode() invoke Arrays.hashCode(array). To implement that extend HashMap and then use Collections.newSetFromMap(map)


Actually, you can. You can use TreeSet with provided Comparator. In your case it will be something like:

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;
});

Under the hood it will looks like alphabetic sorted tree.


You are actually using the default hashCode method returning different values for all your different arrays!

The best way to solve this is either to use a Collection (such as a List or a Set) or to define your own wrapper class such as:

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());
        }
    }
}

This class actually uses pretty much the same hashCode method as the one for the List.

You now handle:

HashSet<StringArray> boog = new HashSet<StringArray>();


You can use TreeSet.

TreeSet uses Comparable.compareTo() or Comparator.compare() instead of hashCode() and equals() to compare elements. It can be specified in the constructor.

public static void main(String[] args) {
    Set<String[]> boog = new TreeSet<>(Arrays::compare);
    boog.add(new String[]{"a", "b", "c"});
    boog.add(new String[]{"a", "b", "c"});
    boog.add(new String[]{"a", "b", "d"});
    for (String[] e : boog)
        System.out.println(Arrays.toString(e));
}

output:

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

Arrays::compare is a Comparator that compares arrays in lexicographical order.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号