Changing Values ​​in a HashSet

I read this question: Changing elements in a set changes the semantics of 'equals'

However, I do not know how to solve the problem, that I can not change the item in the HashSet and delete it later.

I have an example source code:

public static void main(String[] args) { TestClass testElement = new TestClass("1"); Set<TestClass> 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<TestClass> 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)); } } 

Where TestClass is just a POJO that contains a variable (plus getter and setter) and has hashcode () and equals ().

There was a request to show the equals () and hashcode () - methods. They are automatically generated by 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; } 

The result is the following:

 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): 

When the hash code has changed, I cannot remove the value from the HashSet. As in the related question , I understand why this is so, but I do not know how to delete the changed value. Is there any way to do this?

+6
source share
3 answers

You encountered a problem because the keys in your hash set are not immutable. If you do not have immutable keys, you will lose the link to the original key object after its modification. And she will never be able to cope with this, which is sometimes called a memory leak in the collection. Therefore, if you use immutable keys, you will not encounter this situation.

+8
source

As a question that you linked to the details, and as others pointed out, you are facing a mutable key problem. I will write Javadoc :

Note. Great care should be taken if mutable objects are used as set items. The behavior of the set is not indicated if the value of the object is changed in a way that affects equal comparisons, while the object is an element in the set.

As you pointed out, you understand that. The question is, how do you actually delete the object, given this case? You cannot use Set.remove() because your object is lost in the hash table. However, you can use Iterator for this. Something like the following:

 TestClass toRemove = <the same instance, but mutated>; for (Iterator<TestClass> iter = set.iterator(); iter.hasNext(); ) { TestClass item = iter.next(); if (toRemove.equals(item)) { iter.remove(); } } 

This approach is based on the fact that the standard equals() methods, for example, you use, have an instance check, and this check will return true.

Please keep in mind that this is the wrong way to solve this problem. The correct way is either to use an immutable key or to "exercise great care", but this is a way to remove a mutated object from a HashSet .

+2
source

When you add testElement to a HashSet, it selects a hash-based bucket for testElement . When you request a HashSet if it contains testElement , it computes the hash code of the object that it searches and searches only in this bucket.

Since your hashCode() based on a non-final field, the hash code may change behind the scenes of a HashSet. Thus, the basic assumption of the HashSet is completely invalidated.

The correct implementation for Testclass would have the s1 field as final.

+1
source

Source: https://habr.com/ru/post/947719/


All Articles