对于以下代码,它输出“1”。和第二个代码输出“2”我不明白为什么会发生这种情况。是因为我要添加相同的对象吗?我该如何实现所需的输出2。
import java.util.*;
public class maptest {
public static void main(String[] args) {
Set<Integer[]> set = new HashSet<Integer[]>();
Integer[] t = new Integer[2];
t[0] = t[1] = 1;
set.add(t);
Integer[] t1 = new Integer[2];
t[0] = t[1] = 0;
set.add(t);
System.out.println(set.size());
}
}
第二代码:
import java.util.*;
public class maptest {
public static void main(String[] args) {
Set<Integer[]> set = new HashSet<Integer[]>();
Integer[] t = new Integer[2];
t[0] = t[1] = 1;
set.add(t);
Integer[] t1 = new Integer[2];
t1[0] = t1[1] = 1;
set.add(t1);
System.out.println(set.size());
}
}
Set
实现可能调用t.hashCode()
,因为数组不会覆盖Object.hashCode
方法,所以同一个对象将具有相同的哈希码。因此,更改数组的内容不会影响其哈希代码。要正确获取数组的哈希码,您应该调用Arrays.hashCode
。
不管怎么说,你不应该把可变内容放在集合中,所以我建议你把不可变列表放到集合中。如果你想坚持使用数组,只需像使用t1
一样创建一个新数组,并将其放入集合中。
编辑:
对于代码2,t
和t1
是两个不同的数组,因此它们的哈希码是不同的。同样,因为hashCode
方法没有在数组中被覆盖。数组的内容不会影响哈希码,无论它们是否相同。
Set
只包含不同的元素(它的本质)。基本的实现,HashSet
,使用hashCode()
首先找到一个包含值的桶然后equals(Object)
来寻找一个不同的值。
数组很简单:它们的hashCode()使用默认值,继承自Object
,因此取决于引用。 equals(Object)
也与Object
相同:它仅检查识别,即:引用必须等于。
定义为Java:
public boolean equals(Object other) {
return other == this;
}
如果你想要放置不同的数组,你必须要试试运气TreeSet
和Comparator
的正确实现,要么包装你的数组或使用List
或另一个Set
:
Set<List<Integer[]>> set = new HashSet<>();
Integer[] t = new Integer[]{1, 1};
set.add(Arrays.asList(t));
Integer[] t1 = new Integer[]{1, 1};
set.add(Arrays.asList(t1));
System.out.println(set.size());
至于Set
或Map
密钥中使用的对象的可变性:
boolean equals(Object)
使用的字段不应该被静音,因为静音对象可能等于另一个。 Set不再包含不同的值。int hashCode()
使用的字段不应该为基于散列的集合(HashSet
,HashMap
)静音,因为如上所述,它们通过将项目放入桶中来进行操作。如果hashCode()发生变化,那么桶中对象的位置也可能会发生变化:Set
将包含相同引用的两倍。int compareTo(T)
相同的原因,Comparator::compare(T,T)
或equals
使用的字段不应该被静音:SortedSet
不会知道有变化。如果需要,你必须首先从集合中删除项目,然后改变它,重新添加它。
你将Object
添加到Set
中
不包含重复元素。
你只需要向Object
添加一个Set
。您只能更改其内容的值。要了解我的意思,请尝试添加System.out.println(set.add(t));
。
作为add()
方法:
如果此set尚未包含指定的元素,则返回true
此外,您的t1
与您的第一个代码段完全无关,因为您从未使用它。
在你的第二个代码片段中输出两个因为你在Integer[]
中添加了两个不同的Objects
Set
尝试打印出Objects
的哈希码,看看它是如何工作的:
Integer[] t = new Integer[2];
t[0] = t[1] = 1;
//Before we change the values
System.out.println(t.hashCode());
Integer[] t1 = new Integer[2];
t1[0] = t1[1] = 1;
//After we change the values of t
System.out.println(t.hashCode());
//Hashcode of the second object
System.out.println(t1.hashCode());
输出:
//Hashcode for t is the same before and after modifying data
366712642
366712642
//Hashcode for t1 is different from t; different object
1829164700
java.util.Set
实现如何检查重复对象取决于实现,但根据Set
的文档,“重复”的适当含义是o1.equals(o2)
。
由于HashSet
特别基于哈希表,因此它将通过计算呈现给它的对象的hashCode()
来寻找副本,然后遍历相应哈希桶中的所有对象(如果有的话)。
数组不会覆盖hashCode()
或equals()
,因此它们实现实例标识,而不是值标识。因此,无论其元素的值如何,给定的数组总是具有相同的哈希码,并且始终只有equals()
本身。第一个代码将相同的数组对象添加到集合中两次。无论其元素的值如何,它仍然是相同的集合。第二个代码将两个不同的数组对象添加到一个集合中。无论其元素的值如何,它们都是不同的对象。
另请注意,如果您有可变对象实现值标识,这样它们的相等和哈希码取决于其成员的值,那么当它是Set
的成员时修改这样的对象很可能会打破Set
。这是基于每个实现的记录。