我正在尝试从HashSet中删除一个元素,但它不会。
调用.contains(obj)
时,它返回true,因此.contains
知道对象位于HashSet
中。但是当我调用.remove(obj)
时,对象不会从HashSet
中删除,而是返回false。
对象代码 - https://gist.github.com/rincew1nd/f97f11d21ba41d2f4197f9a9da02fea1
答案 0 :(得分:3)
这是因为您没有在班级中覆盖Object#hashCode()
。
Object中hashCode()
的实现返回一个从调用对象内存地址计算的值。因此,除非您使用remove(Object o)
传递对地图中包含的对象的引用,否则HashSet无法找到该对象。
注意:引用与o1.equals(o2)
有关为什么' HashSet#contains(Object o)'即使存储在地图中的对象没有覆盖Object#hashCode()
说我有一个课程MyClass
,不会覆盖hashCode()
MyClass instance1 = new MyClass(); // Memory Address = 0x01
MyClass instance2 = new MyClass(); // Memory Address = 0x02
instance1.equals(instance2) == false
我可以将这些唯一对象添加到HashSet没有问题,因为添加到HashSet只依赖于equals(Object o)
,而不是hashCode()
。
现在让我们说我要将它们从Set中删除但我丢失了对它们的原始引用,但我确实有以下参考:
instance3 // Memory Address = 0x03
instance4 // Memory Address = 0x04
instance1.equals(instance3) == true
instance2.equals(instance4) == true
致电nonHashSet.remove(instance3)
将从集合中删除instance1
。但是基于哈希的集合完全不同:hashCode(自我们使用Object#hashCode()
以来从内存地址计算的值)用于定位项目。因此,当我要求HashSet删除instance3
时,它告诉我哈希索引没有元素。这是因为instance1和instance3的内存地址不同,因此每个的哈希值都是不同的含义。
这真的很糟糕,因为现在从集合中删除instance1
或instance2
的唯一可靠方法是清除整个集合,或者当然重新初始化它。
HashSet
:当您尝试首先添加元素时,HashSet会检查是否已包含另一个被视为等于的元素。如果要添加的元素唯一到所有其他元素,则会将其添加到集合中。
HashSet
与NonHashSet
的区别在于如何存储和检索元素。
在HashSet
中,在将一个元素添加到集合后(如add(E element)
返回true时),通过调用{{1}生成该元素的散列 }}。在非常宽松的术语中,您可以将哈希视为元素将存储在的索引。
取自https://en.wikipedia.org/wiki/Hash_table
我在上面描述的方式element.hashCode()
,key == element
,hash function == element.hashCode()
关于Buckets的说明:与传统存储不同,每个元素都有自己的索引,对于两个不同的元素(不认为彼此相等)产生相同的哈希。发生这种情况时,冲突处理机制用于存储具有相同哈希的元素。这超出了本问题的范围,但处理此问题的一种常见且简单的方法是在碰撞哈希索引中创建一个列表,它存储包含相同哈希的所有元素。
从基于散列的集合中删除元素时,我们作为要删除的参数传递的元素会计算它的散列值,然后使用该散列来定位元素。如果散列索引上有多个元素(当存在具有相同散列的元素时发生),则使用HashSet使用的碰撞处理机制来查找我们正在寻找的确切项目。在上面指定的机制中(在碰撞索引处存储List以存储每个冲突的元素)buckets == indexes
一般合同 elements.equals(collidedElement)
实施应符合:
Hash Code Contract Java SE 8
更多信息:Why do I need to override the equals and hashCode methods in Java?
实现 EFFECTIVE 哈希函数的简单方法:Best implementation for hashCode method
hashCode()
的工作原理:让我们看一下确定HashSet类中contains(Object o)
的返回值的代码(Java Version 7u40-b43)。我已添加评论来描述正在发生的事情:
contains(Object o)