HashSet允许重复元素。

9

我似乎无法让HashSet实例按预期工作。我使用的代码如下:

import testing.Subclass;
import java.util.HashSet;

public class tester {
  public static void main(String[] args) throws Exception {
    HashSet<Subclass> set = new HashSet<Subclass>();
    set.add(new Subclass("007812"));
    set.add(new Subclass("007813"));
    System.out.println("Set size " + set.size());
    set.add(new Subclass("007812"));
    System.out.println("Set size " + set.size());

    for(Subclass sub : set) {
      System.out.println(" sub acctNbr " + sub.getAcctNbr());
    }
  }
}

子类

public class Subclass implements Comparable<Subclass> {

  public Subclass(String acctNbr) {
    this.acctNbr = acctNbr;
  }
  private String acctNbr;
  public String getAcctNbr() {
    return this.acctNbr;
  }
  public int compareTo(Subclass other) {
    return this.getAcctNbr().compareTo(other.getAcctNbr());
  }

  public boolean equals(Subclass other) {
    if(other.getAcctNbr().equals(this.getAcctNbr()))
      return true;
    else
      return false;
  }
  public int hashCode() {
    return acctNbr.hashCode();
  }
}

这段代码输出:
sross@sross-workstation:~/Documents$ javac testing/Subclass.java
sross@sross-workstation:~/Documents$ javac tester.java
sross@sross-workstation:~/Documents$ java tester
Set size 2
Set size 3
 sub acctNbr 007812
 sub acctNbr 007812
 sub acctNbr 007813
sross@sross-workstation:~/Documents$

2
你期望的行为是什么,它与你看到的行为有何不同? - Bill the Lizard
6个回答

22
您需要覆盖 equals(Object) 方法。相反,您实现了一个带有签名 equals(Subclass)equals 方法。因此,您的 HashSet 正在使用在 Object 上定义的默认 equals(Object) 方法进行相等性测试。
默认的 equals(Object) 实现基于对象标识,因此集合“允许”您添加两个语义上相等但不是同一对象的 String

5
别忘了同时重写 hashCode() 方法。 - mR_fr0g
OP已经正确地覆盖了hashCode(),但这仍然是一个重要的观点。 - Adamski

6

你没有正确地覆盖了Object.equals()方法。

@Override
public boolean equals(Object other) {
    if ((other == null) || !(other instanceof Subclass)) {
        return false;
    }
    return ((Sublcass) other).getAcctNbr().equals(this.getAcctNbr());
}

boolean equals(Subclass other)方法创建了第二个方法,这不是您想要做的。


仅将“Boo”作为可选的覆盖标签。 - user166390
3
由于instanceof对于空引用返回false,所以您无需检查null。 - Jonathan Feinberg
同意,严格检查 @Override 会立即指出问题。 - andersoj
你可以通过先检查 this == other 来进行优化。此外,在调用其 getAcctNbr()之前,你需要对 'other' 进行强制转换。 public boolean equals(Object o) { return this == o || (o instanceof Subclass && acctNbr.equals(((Subclass)o).getAcctNbr)); } - Adamski

3

两个要点:

第一,在你认为你正在重写一个方法的时候,养成使用@Override的习惯。这样会导致你的示例代码编译失败,从而发现问题。

第二,如果你正在使用一个IDE,并且它没有为你突出显示一个漂亮的粗体警告,那么它是配置错误的!你应该修复它!

如果你没有使用IDE——你真的,真的应该使用它。一旦你输入了public boolean equals(Subclass other),文本就会改变颜色,并显示一个警告,告诉你可能存在的问题。

顺便说一下,我收敛到的equals()的标准习惯是这样的:

@Override public boolean equals(Object object) {
  if (object instanceof Subclass) {
    Subclass that = (Subclass) object;
    return this.anInt == that.anInt
        && this.aString.equals(that.aString); // for example
  }
  return false;
}

在某些情况下,值得在代码前加上 if (object == this) { return true; } ,但这并不值得成为一种常规习惯。


2

我遇到了几乎相同的问题,正如大家所说,你需要重写正确的public boolean equals(Object o)方法。但这还不够!

还需要重写public int hashCode()(就像你做的那样),否则,Java根本不会调用equals方法。


0

您的equals方法从未被调用。equals的签名要求它接受一个Object,而不是其他类(包括实现equals的任何类)。

public boolean equals(Object other) {
    ...
}

0
首先猜测,看起来你的`equals(Subclass other)`应该改成`equals(Object other)`,以便覆盖`java.lang.Object.equals()`方法,就像你想要的那样。很可能集合正在调用底层的`equals()`实现。

是的,或者更准确地说,就像Bombe所说的那样。 - andersoj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接