java hashset中项的相等性

时间:2011-08-02 06:25:03

标签: java equals hashcode

我需要为一个Item实现equals()方法,该方法可以放在它的Maker的hashset中。Item可以有以下字段

class Item{
    private String isbn;
    private String name;
    private double price;
...
}

class Maker{
    private String name;
    private Set<Item> items;
    public Maker() {
        super();
        items = new HashSet<Item>();
    }
...
}

如果我通过比较三个字段来实现equals并根据这些字段写一个hashCode(),那么我将得到错误的结果

1.add item to hashset
2.modify the price of item
3.try to find if item exists in hashset


@Override
public boolean equals(Object o){
    if(o == this){
        return true;
    }
    if (!(o instanceof Item)){
        return false;
    }
    Item a = (Item)o;
    if(hasSameName(a) && hasSameIsbn(a) && hasSamePrice(a)){
        return true;
    }
    else{
       return false;
    }
}

@Override
public int hashCode(){
    int hash = 41 + this.isbn.hashCode();
    hash = hash*41+ new Double(this.price).hashCode();
    hash = hash*41 + this.name.hashCode();
    return hash;
}

...
Set<Item> items = new HashSet<Item>();
Item item1 = new Item();
item1.setName("crystal bird");
item1.setIsbn("1111");
item1.setPrice(120.5);

items.add(item1);

System.out.println(items.contains(item1));//returns true
item1.setPrice(177.0);
System.out.println(items.contains(item1));//returns false

解决这个问题的解决方案是什么?我应该使hashCode()仅依赖于isbn并假设它在项目的生命周期内不会改变。

任何帮助表示赞赏

标记。

4 个答案:

答案 0 :(得分:5)

是的,ISBN是典型的自然标识符 - 仅用于equals(..)hashCode()

答案 1 :(得分:1)

HashSet.add()方法(基本上是HashMap.put()方法)根据hashcode()找到自维护内部表的最佳位置。

如果更改KeyHashMap项的值,则会更改hashcode()值,从而导致异常结果。

您只应将ISBN视为对象的关键,以使事情尽可能简单。

@Override
public boolean equals(Object o){
    if( o == null ){
        return false;
    }
    if(o == this){
        return true;
    }
    if (!(o instanceof Item)){
        return false;
    }
    Item a = (Item)o;
    if( hasSameIsbn(a) ){
        return true;
    }
    else{
       return false;
    }
}

@Override
public int hashCode(){
    return (41 + this.isbn.hashCode());
}

答案 2 :(得分:1)

在你可能错过的API的某个地方有一个黄金法则:
当对象在HashMap / Set中时,不能更改hashCode!

HashMap / Set使用hashCode以更快的方式排列和查找对象。所以你需要提供一个不会改变的hashCode。 hashCode在每种情况下都不需要是唯一的,但如果是,它将提供更好的访问时间。

其他人已经提出了解决方案:使用ISBN作为hashCode。我提供了一个非常好的独特性,不会改变!

答案 3 :(得分:0)

是的,因为您的价格是hashCode计算的一部分,并且其中评估了对象的相等性,当您设置不同的价格时,它的哈希码会发生变化,因此您无法找到相同的项目。 contains()方法使用equals()来检查一个集合是否包含相同的对象,并且由于你的价格是修改后的等式检查的一个因素,因此不再相等,因此contains()无法找到它