这个 HashSet 测试示例可能吗?

Is this HashSet test example possible?

昨天我进行了一次技术面试,其中一项任务是解释下面的测试示例。这可能吗?在什么情况下?

void test() {
   A a = new A();
   B b = new B();
   C c = new C();

   HashSet set;

   set = new HashSet();
   set.add(a);
   set.add(b);
   set.add(c);
   assert set.size() == 3;

   set = new HashSet();
   set.add(a);
   set.add(c);
   set.add(b);
   assert set.size() == 2;
}

请帮帮我!

根据其规范,HashSet.add 添加一个元素

if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2)

因此,如果 B 和 C 类 的等号运算不对称(c.equals(b)!b.equals(c)),则有可能。
它们的哈希值也需要相等,因为如果我记得正确的话,HashSet 在 .equals 之前检查哈希值。

是的,您可以使用非常丑陋的代码实现该行为! 从来没有像我那样实现equalshashCode(Java)。诀窍在于这些方法应该改变它们的行为:

class A  {
  @Override
  public int hashCode() {
    return 0;
  }

  @Override
  public boolean equals(Object o) {
    return o == this;
  }
}

class B  {
  @Override
  public int hashCode() {
    return 1;
  }

  @Override
  public boolean equals(Object o) {
    return o == this;
  }
}

class C  {
  private static int counter = 0;

  @Override
  public int hashCode() {
    counter += 1;

    return 0;
  }

  @Override
  public boolean equals(Object o) {
    if (counter < 2)
      return o == this;

    return true;
  }
}

Class Cequals 以及 hashCode() 改变 一些调用后的行为。

假设您的意思是让两个断言都避免在这样的程序中失败,如果 A/B/C 以某种方式覆盖 GetHashCodeEquals 是可能的,使得相等运算符是不对称。

为简单起见,我们假设所有元素都被覆盖为 return 相同的哈希码:

b==a -> false
c==a -> false
c==b -> false
b==c -> true

在第一种情况下:

  set.add(a);
  set.add(b);
  set.add(c);

我们首先将a插入到哈希集中。这是给定的,因为它是空的。插入 b 时,哈希集通过比较计算结果为 false 的 b==a 来搜索重复项(因此插入 b)。插入 c 时,has 集计算 c==a 为假,b==a 计算为假(因此插入 c)。

现在来看第二个例子:

   set.add(a);
   set.add(c);
   set.add(b);

第一行相同。第二行比较 c==a 结果为 false,所以 c 被插入。第三行然后比较 b==c 结果为真。结果,b 没有被插入。生成的哈希集大小现在是 2 而不是 3,并且两个断言都成功。

当然,您需要一些非常奇怪的代码来覆盖 GetHashCodeEquals 才能使 asserts 都成功,并且测试将表明一些非常古怪的逻辑(它'如果 确实 通过,d 就是那种表明存在问题的测试)。但这是通过此测试的一种可能方式。