Java 添加等效可变对象的HashSet

Java 添加等效可变对象的HashSet,java,hashset,Java,Hashset,我知道我应该使公共类不可变,但在将Person对象添加到类型集HashSet之前,我已经更改了它的名称,并且我的Person类还实现了hashCode()和equals()方法,这些方法有助于检查对象是否与前一个对象相同,如果在hashCode()和equals()方法的帮助下是等效的,则可以防止将其添加到列表中,但输出仍然是: 上下快速移动 查理 鲍勃 而不是: 上下快速移动 查理 如果替换主类的代码段: Set<Person> set = new HashSet<Perso

我知道我应该使公共类不可变,但在将Person对象添加到类型集
HashSet
之前,我已经更改了它的名称,并且我的Person类还实现了
hashCode()
和equals()方法,这些方法有助于检查对象是否与前一个对象相同,如果在
hashCode()
和equals()方法的帮助下是等效的,则可以防止将其添加到列表中,但输出仍然是: 上下快速移动 查理 鲍勃

而不是: 上下快速移动 查理

如果替换主类的代码段:

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);
Set Set=newhashset();
人物a=新人(“爱丽丝”,45岁);
人员b=新人员(“bob”,41);
人员c=新人(“查理”,48岁);
增加(a);
a、 name=“bob”;
增加(b);
增加(c);
通过下面的代码(即,我直接声明了一个等价的对象,而没有像最初的问题代码那样更改名称):

Set Set=newhashset();
人员a=新人员(“bob”,45岁);
人员b=新人员(“bob”,41);
人员c=新人(“查理”,48岁);
增加(a);
增加(b);
增加(c);
然后不添加对象b

还有一点需要注意的是,在添加对象b之前,我更改了对象a的名称(在原始有问题的代码中),但它仍然在添加b为什么

我最初的有问题的代码包含主类和公共类,详细内容如下:

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

        }



    }


}
//泛型的Main.java类\u实践\u测试包;
包装仿制药\实践\测试;
导入java.util.Set;
导入java.util.HashSet;
导入java.util.Iterator;
公共班机{
公共静态void main(字符串参数[])
{
Set=newhashset();
人物a=新人(“爱丽丝”,45岁);
人员b=新人员(“bob”,41);
人员c=新人(“查理”,48岁);
增加(a);
a、 name=“bob”;
增加(b);
增加(c);
for(Iterator Iterator=set.Iterator();Iterator.hasNext();){
System.out.println(iterator.next());
}
}
}
Person类的代码如下所示: //下面是泛型\实践\测试包的人员类别

package generics_practice_test;

public class Person implements Comparable<Person> {

    public String name;

    public int age;

    public Person(String name,int age)
    {
        this.name=name;
        this.age=age;
    }

    public String toString()
    {
        return this.name;
    }

    public int compareTo(Person o)
    {
        int myLength=name.length();
        int oLength=o.name.length();
        if(myLength == oLength) return 0;
        if(oLength > myLength) return -1;
        return 1;

    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            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;
    }

}
package-generics\u-practice\u-test;
公共类人员实现可比性{
公共字符串名称;
公共信息;
公众人物(字符串名称,整数年龄)
{
this.name=name;
这个。年龄=年龄;
}
公共字符串toString()
{
返回此.name;
}
公共内部比较(o人)
{
int myLength=name.length();
int-olelength=o.name.length();
如果(myLength==oleLength)返回0;
if(olelength>myLength)返回-1;
返回1;
}
@凌驾
公共int hashCode(){
最终整数素数=31;
int结果=1;
result=prime*result+((name==null)?0:name.hashCode();
返回结果;
}
@凌驾
公共布尔等于(对象obj){
if(this==obj)
返回true;
if(obj==null)
返回false;
如果(getClass()!=obj.getClass())
返回false;
人员其他=(人员)obj;
if(name==null){
if(other.name!=null)
返回false;
}如果(!name.equals(other.name))
返回false;
返回true;
}
}

尝试将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
A
HashSet
将根据
hashCode
方法的结果存储元素。这是实现中使用的伪算法:

  • 检索对象
    hashCode
    。将其存储在
    int-possibleIndex
  • 根据
    possibleIndex
    获取可存储对象的索引。将其存储在
    索引中
  • 检索存储在
    索引中的
    列表
    。遍历此列表,并使用
    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);
//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);

HashSet
在内部使用
HashMap
,HashMap是
hashtable
数据结构的实现

Hashtables
uses有一个将数据存储在bucket中的概念

把它想象成一个映射,其中键是
hashCode
,值是元素列表

现在这里发生的是,在存储时,它获取
hashCode()
,并尽可能存储当前元素

在您的例子中,它是
name
hashCode。现在以bob的哈希值为32为例,因此将有一个以32为键的条目,列表将包含该元素

您更改的名称并不会真正更改元素的位置

插入HashSet时,请遵循以下步骤

  • 获取对象的hashCode值
  • 用该值搜索bucket
  • 如果找到了bucket,则遍历列表并使用当前对象对所有bucket调用equals
  • 如果找到对象,则不要插入它,否则请插入它

  • a.name=“bob”
    之后仍然添加
    b
    ,因为
    a
    与其旧散列一起存储。更改
    a.name
    会更改
    a.hashCode()
    返回值,但包含的
    HashSet
    无法知道这一点——因此
    a
    仍使用其旧哈希(即
    //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);