更改HashSet中的值

我已经读过这个问题: 改变集合中的元素会改变’equals’语义

但是,我不知道如何解决我无法更改HashSet中的项目并在以后删除它的问题。

我有一些示例源代码:

public static void main(String[] args) { TestClass testElement = new TestClass("1"); Set set = new HashSet(); set.add(testElement); printIt(testElement, set, "First Set"); testElement.setS1("asdf"); printIt(testElement, set, "Set after changing value"); set.remove(testElement); printIt(testElement, set, "Set after trying to remove value"); testElement.setS1("1"); printIt(testElement, set, "Set after changing value back"); set.remove(testElement); printIt(testElement, set, "Set removing value"); } private static void printIt(TestClass hullo, Set set, String message) { System.out.println(message + " (hashCode is " + hullo.hashCode() + "):"); for (TestClass testClass : set) { System.out.println(" " + testClass.toString()); System.out.println(" HashCode: " + testClass.hashCode()); System.out.println(" Element is equal: " + hullo.equals(testClass)); } } 

其中TestClass只是一个包含变量(加上getter和setter)并且实现了hashcode()和equals()的POJO。

有一个请求显示equals()和hashcode() – 方法。 这些是由eclipse自动生成的:

 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((s1 == null) ? 0 : s1.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TestClass other = (TestClass) obj; if (s1 == null) { if (other.s1 != null) return false; } else if (!s1.equals(other.s1)) return false; return true; } 

结果如下:

 First Set (hashCode is 80): TestClass [s1=1] HashCode: 80 Element is equal: true Set after changing value (hashCode is 3003475): TestClass [s1=asdf] HashCode: 3003475 Element is equal: true Set after trying to remove value (hashCode is 3003475): TestClass [s1=asdf] HashCode: 3003475 Element is equal: true Set after changing value back (hashCode is 80): TestClass [s1=1] HashCode: 80 Element is equal: true Set removing value (hashCode is 80): 

当哈希码发生变化时,我无法从HashSet中删除该值。 在链接的问题中 ,我理解为什么会这样,但我不知道如何删除更改的值。 有没有可能这样做?

您遇到了问题,因为哈希集中的键不是不可变的。 如果您没有不可变密钥,则修改后将丢失原始密钥对象的引用。 并且永远无法处理它,有时也称为集合中的内存泄漏。 因此,如果您使用不可变密钥,则不会遇到这种情况。

正如您与细节相关联的问题,以及其他人指出的那样,您遇到了可变的密钥问题。 我将从Javadoc重新引用 :

注意:如果将可变对象用作set元素,则必须非常小心。 如果在对象是集合中的元素的同时以影响等于比较的方式更改对象的值,则不指定集合的​​行为。

正如你所指出的,你明白了。 问题是,在具体情况下,你如何实际删除对象? 您不能使用Set.remove() ,因为您的对象在哈希表中丢失。 但是,您可以使用Iterator来执行此操作。 类似于以下内容:

 TestClass toRemove = ; for (Iterator iter = set.iterator(); iter.hasNext(); ) { TestClass item = iter.next(); if (toRemove.equals(item)) { iter.remove(); } } 

这种方法依赖于标准的equals()方法,就像你正在使用的那样,有一个实例检查,并且该检查将返回true。

请记住,这不是解决此问题的正确方法。 正确的方法是使用不可变的键或“非常小心”,但它是一种从HashSet删除变异对象的方法。

testElement添加到HashSet时,它会根据testElement的哈希码选择一个存储桶。 当您询问HashSet是否包含TestElement ,它会计算它正在查找的对象的哈希码,并仅在该桶中进行搜索。

由于您的hashCode()基于非final字段,因此哈希代码可以在HashSet的幕后更改。 从而完全使HashSet的基本假设无效。

Testclass正确实现将使s1字段成为final。