我知道我应该使公共类不可变但仍然在将Person对象添加到类型HashSet
的Set集之前更改了它的名称,而我的Person类也实现了{{1 }和equals()方法有助于检查天气对象是否与前一个对象相同,如果在hashCode()
和equals()方法的帮助下等效,则阻止添加到列表中,但是仍然输出是:
短发
查理
鲍勃
代替: 短发 查理
如果我替换主类的代码段:
hashCode()
通过下面的代码(即我直接声明了一个等效对象,而不是像原始有问题的代码中那样更改名称):
Set<Person> set = new HashSet<Person>();
Person a= new Person("alice",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
set.add(a);
a.name="bob";
set.add(b);
set.add(c);
然后不添加对象b。
另外需要注意的一点是,我在添加对象b之前更改了对象的名称(在原始有问题的代码中)但是仍然添加了b为什么????
我的原始有问题的代码包含Main Class,后跟Public Class,详细信息如下:
Set<Person> set = new HashSet<Person>();
Person a= new Person("bob",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
set.add(a);
set.add(b);
set.add(c);
Person Person的代码如下: // generics_practice_test包的person类位于
之下//Main.java class of generics_practice_test package;
package generics_practice_test;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
public class Main {
public static void main (String args[])
{
Set<Person> set = new HashSet<Person>();
Person a= new Person("alice",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
set.add(a);
a.name="bob";
set.add(b);
set.add(c);
for(Iterator<Person> iterator=set.iterator();iterator.hasNext();){
System.out.println(iterator.next());
}
}
}
答案 0 :(得分:2)
HashSet
将根据hashCode
方法的结果存储元素。这是在实现中使用的伪算法:
hashCode
。将其存储在int possibleIndex
。possibleIndex
获取可以存储对象的索引。将其存储在index
。List
中存储的index
。遍历此列表并使用equals
方法查找对象是否不在此列表中。这样做是为了处理碰撞或具有相同hashCode
的对象。请注意,对于您的示例,Person#hashCode
实现仅依赖于name
元素。
让我们回顾一下您的第一段代码:
//create the new HashSet
Set<Person> set = new HashSet<Person>();
//create your elements to be inserted
Person a= new Person("alice",45);
Person b= new Person("bob",41);
Person c= new Person("charlie",48);
//try to add "a". It will calculate hashCode from name field,
//which value is "alice"
set.add(a);
//you change the name here, but it won't affect the previous
//operation because the index for "a" was calculated using "alice",
//not "bob" and IT WONT BE RECALCULATED!
a.name="bob";
//try to add "b". It will calculate hashCode from name field,
//which value is "bob"
//as noted before, there's no index based on "bob"'s hashCode,
//so it will be added with no problems
set.add(b);
//try to add "c". It will calculate hashCode from name field,
//which value is "charlie"
set.add(c);
因此,插入所有元素都没有问题,因为hashCode
没有碰撞(这就是为什么之前的解释不能覆盖对equals
的调用的原因法)。
现在,让我们回顾一下您的第二段代码:
//create the new HashSet
Set<Person> set = new HashSet<Person>();
//create your elements to be inserted
Person a= new Person("bob",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
//try to add "a". It will calculate hashCode from name field,
//which value is "bob"
set.add(a);
//try to add "b". It will calculate hashCode from name field,
//which value is "bob"
//since the index calculated from the hashCode of "bob" is
//already inserted, it will check if the element already exists
//Looking at Person#equals, which is based on name field only
//there is an element where the name field has a value of "bob"
//"b" won't be inserted
set.add(b);
//try to add "c". It will calculate hashCode from name field,
//which value is "charlie"
set.add(c);
答案 1 :(得分:0)
HashSet
内部使用HashMap
,HashMap是hashtable
数据结构的实现。
Hashtables
使用了一个将数据存储在存储桶中的概念。
将其想象为地图,其中key为hashCode
,值为元素列表。
现在发生的事情是,在存储时,它会获得hashCode()
并将当前元素存储在任何可能的位置。
在你的情况下,它是name
hashCode。现在举一个例子,你的bob的哈希值是32,所以会有一个以32为键的条目,list将包含该元素。
您更改的名称并未真正改变元素的位置。
插入HashSet时遵循以下步骤。
答案 2 :(得分:-1)
尝试将您的equals方法更改为此
@Override
public boolean equals(Object obj) {
if (obj == null) //first you check if object is null
return false;
if (getClass() != obj.getClass()) // then you check if object is of the same class
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
语句if (this == obj)
按内存中的地址检查对象。内存中的每个对象的地址都会有所不同!如果你在对象上使用了new