将具有相同值的数组添加到HashSet会导致重复项

时间:2013-05-20 20:34:44

标签: java arrays arraylist hashset

我正在尝试创建一组int的数组,问题是如果我尝试这样做:

HashSet<int[]> s = new HashSet<int[]>();
int a1[] = {1,2,3};
int a2[] = {1,2,3};
s.add(a1);
s.add(a2)
System.out.println(s.size());

然后s有两个对象,但应该只有一个。 注意:如果它是HashSet&lt;无关紧要。整数[]取代。它只是不起作用。

现在如果我尝试使用ArrayList&lt;整数&gt;,类似于:

HashSet<ArrayList<Integer>> s = new HashSet<ArrayList<Integer>>();
ArrayList<Integer> a1 = new ArrayList<Integer>();
ArrayList<Integer> a2 = new ArrayList<Integer>();
a1.add(1);
a1.add(2);
a1.add(3);

a2.add(1);
a2.add(2);
a2.add(3);

s.add(a1);
s.add(a2)
System.out.println(s.size());

然后s有一个对象。

我虽然可以避免第一个代码中的错误,并且将每个数组的哈希代码存储在哈希集中,如下所示:

int a1[] = {0,10083,10084,1,0,1,10083,0,0,0,0};
int a2[] = {1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0,2112};
HashSet<Integer> s= new HashSet<Integer>();//hashcodes of each array
s.add(Arrays.hashCode(a1));
s.add(Arrays.hashCode(a2));
System.out.println(Arrays.hashCode(a1));
System.out.println(Arrays.hashCode(a2));
System.out.println(s.size());

它适用于第一种情况(1,2,3)但是在存在碰撞的情况下它不起作用,所以我将不得不管理碰撞。所以,我认为我正在做的是自己实现一个HashSet。

使用HashSet&lt;的ArrayList&LT;整数&GT;&GT;它完美地运作。我认为在这种情况下java会管理冲突。

我的问题是为什么java不允许管理HashSet&lt; INT [] GT;或HashSet&lt;整数[]&GT;如果生成的哈希码与ArrayList中的相同&lt;整数&GT;和数组的哈希码可以通过调用Arrays.hashCode(...)来计算。

最后,如果我想做一个HashSet&lt; int []&gt;(或HashSet&lt; Integer []&gt;)我必须自己实现它吗?或者有更好的方法吗?

感谢。

更新:好的,最后我想我已经得到了一个完整的答案。由于@ZiyaoWei和@ user1676075评论它不起作用,因为equals返回false而hashcode是不同的。但是,为什么java不会覆盖这些方法(使用Arrays.equals(),Arrays.hashCode()),所以可以做一些像HashSet&lt; INT [] GT ;?答案是因为数组是一个可变对象,并且根据哈希码的一般契约,哈希码不能依赖于可变值(数组的每个元素都是可变值)。 Mutable objects and hashCode

这里有很好的解释,在hashCode http://blog.mgm-tp.com/2012/03/hashset-java-puzzler/中使用可变字段,在hashmaps中使用可变键Are mutable hashmap keys a dangerous practice?

我的回答是,如果你想使用HashSet&lt; INT [] GT;您必须创建一个具有数组的类,如果您希望该hashcode和equals依赖于值,则使用Arrays.equals()和Arrays.hashCode()重写方法equals()和hashCode()。如果您不想违反合同,只需将阵列最终确定。

谢谢大家!

2 个答案:

答案 0 :(得分:8)

它与一天结束时的碰撞无关:

a1.equals(a2) == false

由于它们不相等,Set会将它们视为不同。

注意Java中的Array不会覆盖equals中的Object方法。

由于add中的Set被定义为

  

更正式地说,如果集合中不包含元素e2,则将指定的元素e添加到此集合中(e == null?e2 == null:e.equals(e2))

似乎无法正确实施可能符合您要求的Set(与Arrays.equals比较元素)而不违反某些合同。

答案 1 :(得分:1)

HashSet的原因&gt;作品是因为HashSet将使用.equals()比较来决定你是否插入两次相同的对象。在List的情况下,具有相同内容的两个具有相同基本类型的列表(例如,ArrayList)将以相同的顺序进行比较。因此,您告诉HashSet两次插入相同的对象。它只需要一次实例。

当您尝试使用数组执行相同的操作时。有关Java中数组比较的更多详细信息,请参阅此文章:equals vs Arrays.equals in Java。当您插入两个数组时,默认的.equals()测试它们是否是同一个对象,它们不是。因此失败了。