16

当 equals 方法说它们是相同的时,HashSet 只存储值。那正是我所想。

但是现在我将 Elements 添加到 HashSet 中,其中 equals 方法返回 true 并且集合的大小仍在增长?对不起,我很困惑。我错了的一些提示会很好。

Element t1 = new Element(false, false, false, false);
Element t2 = new Element(true, true, true, true);
Element t3 = new Element(false, false, false, false);

if (t1.equals(t3))
    System.out.println("they're equal");

Set<Element> set = new HashSet<>();

set.add(t1);
set.add(t2);
set.add(t3);

System.out.println("set size: " + set.size());

所以在这个例子中我的控制台输出是:

它们的
大小相等:3

这对我来说毫无意义..尺寸应该是2吗?

4

4 回答 4

25

问题是你的Element类没有覆盖equalsandhashCode方法或者这些实现被破坏了。

Object#equals方法 javadoc:

equals 方法在非空对象引用上实现等价关系:

  • 它是自反的:对于任何非空引用值 x,x.equals(x) 应该返回 true。
  • 它是对称的:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应该返回 true。
  • 它是可传递的:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true,则 x.equals(z) 应该返回 true。它是一致的:对于任何非空引用值 x 和 y,-x.equals(y) 的多次调用始终返回 true 或始终返回 false,前提是没有修改对象上 equals 比较中使用的信息。
  • 对于任何非空引用值 x,x.equals(null) 应该返回 false。

Object#hashCode方法 javadoc:

hashCode 的一般合约是:

  • 每当在 Java 应用程序执行期间对同一个对象多次调用它时,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。
  • 如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
  • 如果根据 equals(java.lang.Object) 方法,如果两个对象不相等,则不需要对两个对象中的每一个调用 hashCode 方法都必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

确保这些方法的实现满足这些规则,并且您的Set(由 a 支持HashSet)将按预期工作。

于 2013-04-26T13:52:25.243 回答
4

您的对象具有不同的哈希值,因此 HashSet “放入”然后放入不同的“桶”中。

于 2013-04-26T13:56:07.997 回答
4

如果您有自己的模型类,则需要更改一些基本功能,如下例所示。

执行代码:

HashSet<MyModel> models = new HashSet<MyModel>();

for (int i = 1; i < 5; i++)
    models.add(new MyModel(i + "", "Name :" + i + ""));

for (int i = 3; i < 5; i++)
    models.add(new MyModel(i + "", "Name :" + i + ""));

for (Object object : models)
    System.out.println(object);

模型类:

/**
 * Created by Arun
 */
public static class MyModel {

    private String id = "";
    private String name = "";

    public MyModel(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return getId();
    }

    @Override
    public boolean equals(Object obj) {
        return !super.equals(obj);
    }

    public int hashCode() {
        return getId().hashCode();
    }

}

希望这可以帮助。

于 2015-04-20T11:31:40.953 回答
2

是的,我们可以使用非FINAL类的对象来实现它。

HashSet在添加任何对象之前hashCode()检查两种方法。equals()首先它检查方法hashCode(),如果它返回与 Set 中的任何对象相同的哈希码,然后检查该对象的 equals 方法,该方法在内部比较两个对象的引用,即 this.obj1==obj。如果这些是相同的引用在这种情况下,它返回true意味着它是一个重复值。我们可以通过覆盖 HashCode 和 equals 方法来添加重复的非最终对象。在 HashCode() 中,您可以在参数相同的情况下返回相同的哈希码。

参见示例:

public class Product {
int i;
Product(int a)
{
    this.i=a;
}
@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + i;
    return result;
}
@Override
public boolean equals(Object obj) {
    /*if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Product other = (Product) obj;
    if (i != other.i)
        return false;
    return true;*/
    return true;
}
}
`

`
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
    Product p1=new Product(1);
    Product p2=new Product(1);
    Product p3=new Product(1);
    Set s=new HashSet();
    s.add(p1);
    s.add(p2);
    s.add(p3);
    System.out.println(s.size());
}
}

输出将为 1。

PS:如果不覆盖这些方法,输出将为 3,因为它将使用它们的默认行为。

于 2014-09-08T10:37:34.680 回答