这个 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 之前检查哈希值。
是的,您可以使用非常丑陋的代码实现该行为! 从来没有像我那样实现equals
和hashCode
(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 C
的 equals
以及 hashCode()
改变 一些调用后的行为。
假设您的意思是让两个断言都避免在这样的程序中失败,如果 A/B/C
以某种方式覆盖 GetHashCode
和 Equals
是可能的,使得相等运算符是不对称。
为简单起见,我们假设所有元素都被覆盖为 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,并且两个断言都成功。
当然,您需要一些非常奇怪的代码来覆盖 GetHashCode
和 Equals
才能使 asserts
都成功,并且测试将表明一些非常古怪的逻辑(它'如果 确实 通过,d 就是那种表明存在问题的测试)。但这是通过此测试的一种可能方式。
昨天我进行了一次技术面试,其中一项任务是解释下面的测试示例。这可能吗?在什么情况下?
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 之前检查哈希值。
是的,您可以使用非常丑陋的代码实现该行为! 从来没有像我那样实现equals
和hashCode
(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 C
的 equals
以及 hashCode()
改变 一些调用后的行为。
假设您的意思是让两个断言都避免在这样的程序中失败,如果 A/B/C
以某种方式覆盖 GetHashCode
和 Equals
是可能的,使得相等运算符是不对称。
为简单起见,我们假设所有元素都被覆盖为 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,并且两个断言都成功。
当然,您需要一些非常奇怪的代码来覆盖 GetHashCode
和 Equals
才能使 asserts
都成功,并且测试将表明一些非常古怪的逻辑(它'如果 确实 通过,d 就是那种表明存在问题的测试)。但这是通过此测试的一种可能方式。