HashSet添加等效的可变对象

时间:2014-07-30 16:02:30

标签: java hashset

我知道我应该使公共类不可变但仍然在将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());

        }



    }


}

3 个答案:

答案 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时遵循以下步骤。

  • 获取对象的hashCode值
  • 使用该值搜索存储桶。
  • 如果找到了存储桶,则遍历列表并使用当前对象调用所有存储桶上的等号。
  • 如果找到了对象,则不要插入它,否则插入它。

答案 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