Given the following snippet, how do we effectively remove elements on which have been previously updated/changed?
public static class Foo {
@Override
public int hashCode() {
return new Random().nextInt();
}
}
public static void main(String[] args) {
Set<Foo> set = new HashSet<>();
set.add(new Foo());
set.removeIf(f -> true); // Returns true, but no deletion occurs
assert set.size() == 0; // Fails as set still contains it's single item
}
Note: The above snippet is intended to simulate a different Foo
upon next call to Object::hashCode
(on Set::remove and Set::removeIf).
EDIT:
For those who did not understand the "random hash" part, here is a different view of the problem stated above:
public static class Bar {
public String firstName;
public String lastName;
public Bar() {
this(null, null);
}
public Bar(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public int hashCode() {
int result = 1;
result *= 59 + (firstName == null ? 43 : firstName.hashCode());
result *= 59 + (lastName == null ? 43 : lastName.hashCode());
return result;
}
}
public static void main(String[] args) {
Set<Bar> set = new HashSet<>();
String originalFirstName = "FOO";
String updatedFirstName = "FOO_CHANGED";
// Create bar
Bar bar = new Bar();
bar.firstName = originalFirstName;
bar.lastName = "BAR";
// Add bar
set.add(bar);
// Change bar
System.out.println("Bar hash (now): " + bar.hashCode());
bar.firstName = updatedFirstName;
System.out.println("Bar hash (new): " + bar.hashCode());
Bar oldBar = new Bar(originalFirstName, bar.lastName);
Bar changedBar = new Bar(bar.firstName, bar.lastName);
System.out.println("Old bar hash: " + oldBar.hashCode()); // Hash matches old value
System.out.println("Changed bar hash: " + changedBar.hashCode()); // Hash matches new value
set.remove(oldBar); // Removes no elements (returns false)
set.remove(changedBar); // Removes no elements (returns false)
set.removeIf(f -> true); // Removes no elements (returns true)
Iterator<Bar> iterator = set.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove(); // Fails silently
}
assert set.size() == 0;
}
There's no random hash at all.
There are different hashes indeed, but apparently the elements can never be removed if they have ever been changed (therefore, their hash), regardless what. We can confirm that on both Set::remove
calls, where set.remove(oldBar)
should have removed the element as oldBar
hash equals to the hash when bar
was added.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…