alexanoid alexanoid - 2 months ago 12x
Java Question

HashSet equals true but contains false

I don't understand the following situation:

private Set<Criterion> criteria = new HashSet<>();

public void removeCriterion(Criterion criterion) {
for (Criterion criterion1 : criteria) {
System.out.println(criterion1.equals(criterion) + " : " + criterion.equals(criterion1) + " : " + criterion1.hashCode() + " : " + criterion.hashCode());
System.out.println("contains: " + criteria.contains(criterion));

the output of this code:

true : true : 161 : 161
false : false : 164 : 161
false : false : 163 : 161
false : false : 166 : 161
false : false : 165 : 161
contains: false

For a some reason I can't remove element from the
. Even
method also return false.

What am I doing wrong ?


Criterion extends Authorable extends BaseEntity


public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof BaseEntity))
return false;

BaseEntity that = (BaseEntity) o;

if (id != null) {
if (!id.equals(
return false;
} else {
if ( != null)
return false;

return true;

public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;


While you have not provided a complete verifiable example that would let us reproduce the problem, based on the symptoms you describe it's very likely that you're changing the ID of a criterion after it has been stored in the HashSet, making its hash code change and thus making remove() unable to find the criterion in the hash table (since it will no longer be in the correct bucket).

In general, modifying elements stored in a HashSet (or a HashMap or any other hash-based data structure) in a way that changes their hashCode() violates the contract of the container class, and leads to undefined and generally broken behavior. In particular, the Set documentation says:

Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.

and the Object.hashCode() documentation says that:

Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.

Taken together, these two statement imply that changing an object's hashCode() while it is an element of a Set (including a HashSet) is a contract violation, and will lead to undefined behavior.

As for fixing this, the solution is to stop changing your criterion IDs. You can enforce this by making the id member variable of your BaseEntity class final (and setting its value in the constructor).