我一直在为OCJP(前SCJP)学习,我遇到了以下使用LinkedHashSet的示例:
public class Test{
int size;
public Test(int s){
this.size = s;
}
@Override
public boolean equals(Object obj) {
return (this.size == ((Test)obj).size);
}
public static void main(String[] args) {
LinkedHashSet<Test> s = new LinkedHashSet<Test>();
s.add(new Test(1));
s.add(new Test(2));
s.add(new Test(1));
System.out.println(s.size());
}
}
现在,问题是如果出现的内容:
1)实施保持原样
2)hashCode的重写被插入类Test中,如下所示:
public int hashCode() {return size/5};
运行和编译代码表明第一种情况下set的大小是3,而在第二种情况下它是2。 为什么呢?
在情况1中,尽管equals方法被覆盖,但从不调用它。这是否意味着如果不覆盖hashCode方法,add()方法不会检查对象是否相等? 在情况2中,具有给定实现的hashCode和给定的Test对象集始终返回相同的数字。这与默认的hashCode实现有什么不同,为什么它会导致调用equals?
答案 0 :(得分:10)
如果您不覆盖hashCode()
,那么您的每个实例都会根据{{1}中的某些预定义哈希算法计算出哈希码 }类。因此,您的所有实例都可能具有不同的哈希码值(但这不是必须的)。意味着,每个实例都将进入自己的桶。
现在,即使您重写了Object
方法,根据某些属性使两个实例相等,它们的哈希码仍然不同。
因此,具有不同哈希码的两个实例永远不会相等。所以的集合的大小是3.因为它没有任何重复。
但是,当您通过以下实施覆盖equals()
时: -
hashCode()
它将为同一public int hashCode() {return size/5};
返回相同的值。因此,具有相同值size
的实例将具有相同的哈希码,并且,因为您已基于size
在equals
方法中对它们进行了比较,因此它们是将是size
,因此它们将在equal
中被视为重复,因此将被删除。因此,Set
为2。
道德: - 每当覆盖Set.size()
方法时,都应该覆盖hashCode()
,以维持两种方法之间的一般契约。
equals()
和hashcode
方法之间的一般合同: -
equals
来计算用于比较两个实例的same attributes
强烈建议至少阅读一次: -
答案 1 :(得分:2)
哈希结构依赖于哈希算法,该算法在java中由hashCode()
表示。当您将某些内容放入HashMap
(或者LinkedHashSet
中)时,jvm会对插入此结构的对象调用hashCode()
。当它没有被覆盖时,将使用来自hashCode()
类的默认Object
并且效率低 - 所有对象都进入它们自己的桶中。
当您以示例中显示的方式覆盖hashCode()
时,示例中的所有对象都将进入同一个存储桶。然后(当一个接一个地添加它们时),与equals()
进行比较。这就是为什么在第一种情况下(当没有调用equals()
时)你得到3的大小,而在第二种情况下 - 2。