public static void main(String[] args) {
HashSet set = new HashSet();
set.add(new StringBuffer("abc"));
set.add(new StringBuffer("abc"));
set.add(new StringBuffer("abc"));
set.add(new StringBuffer("abc"));
System.out.println(set);
}
输出:
[abc,abc,abc,abc]
在上面的代码中,我多次添加了StringBuffer("abc")
的对象,Set
添加了它,但Set从不添加重复项。
答案 0 :(得分:13)
StringBuffer
does not override Object#equals()
and Object#hashCode()
,因此StringBuffer
实例的标识基于而不是对缓冲区的内容,而是基于对象在内存中的地址。*
* JLS不严格要求该标识基于内存中的地址,但这是典型Object#hashCode()
实现的结果。来自JavaDoc:
尽管合理可行,但是由类
Object
定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但Java™编程语言不需要此实现技术。)
答案 1 :(得分:8)
StringBuffer
不会覆盖equals
或hashCode
- 所以每个对象只等于它自己。
这是有道理的,因为StringBuffer
非常“设计可变” - 当两个可变对象彼此相等时,相等可能会导致问题,然后可以改变。将可变对象用作映射中的键或集合的一部分可能会导致问题。如果在插入集合后改变了一个,则会使集合中的条目无效,因为哈希代码可能会更改。例如,在地图中,您甚至无法使用相同对象作为键来查找值,因为第一个测试是通过哈希码进行的。
StringBuffer
(和StringBuilder
)被设计成非常短暂的对象 - 创建它们,附加到它们,将它们转换为字符串,然后就完成了。每当你发现自己将它们添加到集合中时,你需要退一步看看它是否真的有意义。只是偶尔可能会这样做,但通常只有当集合本身短缺时才会这样做。
在覆盖equals
和hashCode
时,您应该在自己的代码中考虑这一点 - 基于对象的任何可变方面,相等很少是一个好主意。它使得类更难以正确使用,并且很容易导致细微的错误,这可能需要很长时间才能进行调试。
答案 2 :(得分:1)
您是否发现在StringBuffer中看到equals()方法(或缺少它)?答案就在于你。
Set或者就此而言,任何基于散列的集合都取决于Object上的equals()和hashcode()方法为其行为特征所公开的契约。
在你的情况下,由于StringBuffer没有覆盖这些方法,你创建的每个StringBuffer实例都是不同的,即new StringBuffer(“abc”)== new StringBuffer(“abc”)将返回false。
我很好奇为什么有人会将StringBuffer添加到一个集合中。
答案 3 :(得分:1)
大多数可变对象不假设如果它们碰巧包含相同的数据,则它们是相同的。由于它们是可变的,您可以随时更改内容。即它现在可能是相同的,但不会更晚,或者现在可能会有所不同,但稍后会相同
BTW如果StringBuilder是一个选项,你不应该使用StringBuffer。 StringBuffer在十多年前被取代了。
答案 4 :(得分:0)
尽管具有相同的参数,但两个StringBuffer对象是不同的对象。因此,HashSet只是添加StringBuffers而不是忽略重复项。
答案 5 :(得分:0)
哈希集与“桶”一起使用。它根据哈希码将值存储在那些“桶”中。根据这些成员是否相同,“桶”可以包含多个成员,使用equals(Object)
方法。
因此,假设为了参数,我们构造了一个包含10个桶的哈希集,并将整数1,2,3,5,7,11和13添加到它中。 int的哈希码就是int。我们最终会得到这样的结论:
使用集合的传统方法是查看成员是否在该集合中。所以,当我们说,“这套是11?”哈希集将模数为11乘10,得到1,然后查看第二个桶(当然我们用0开始我们的桶)。
这使得查看成员是否属于某个集合确实非常快速。如果我们添加另一个 11,则哈希集会查看它是否已存在。如果是的话,它不会再添加它。它使用equals(Object)
方法来确定,当然,11等于11。
像“abc”这样的字符串的哈希码取决于该字符串中的字符。当您添加重复的字符串“abc”时,哈希集将查找右侧存储桶,然后使用equals(Object)
方法查看该成员是否已存在。字符串的equals(Object)
方法也取决于字符,因此“abc”等于“abc”。
但是,当您使用StringBuffer时,每个StringBuffer都有一个基于其Object ID的哈希码和相等性。它不会覆盖基本的equals(Object)
和hashCode()
方法,因此每个StringBuffer都会像其他对象一样查看哈希集。它们实际上并不重复。
当您将StringBuffers打印到输出时,您将在StringBuffers上调用toString()方法。这使它们看起来像重复的字符串,这就是你看到输出的原因。
这也是为什么在覆盖hashCode()
时覆盖equals(Object)
非常重要的原因,否则Set会查找错误的存储桶,并且会出现一些奇怪且不可预测的行为!