Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HashSet.contains returns false when it shouldn't

I have this code:

public class Tray {

private Set<Block> blocks;

private int numColumns;
private int numRows;

//constructor
public Tray (int numRows, int numColumns){
    this.numColumns = numColumns;
    this.numRows = numRows;
    blocks = new HashSet<>();
}

public boolean satisfiesGoal(Tray other){

    Block thisBlock = this.blocks.iterator().next();
    Block otherBlock = other.blocks.iterator().next();

    boolean hashesEqual = thisBlock.hashCode() == otherBlock.hashCode(); // this is true

    boolean areEqual = thisBlock.equals(otherBlock) && otherBlock.equals(thisBlock); // this is true

    boolean contains = this.blocks.contains(otherBlock); // this is false, why?
    return contains;
}

In the main method I have added the 2 Blocks to their respective Trays. According to the debugger, the variables "hashesEqual" and "areEqual" are true, but the boolean "contains" is false. Any ideas as to why the hashes of 2 objects would be equal as well as equal according to the "equals" method, but would not contain the equal object in a HashSet?

like image 927
pornstache Avatar asked Apr 22 '17 00:04

pornstache


1 Answers

This problem happens if you modify the objects in a way that affects their equality and hash codes after adding them to the HashSet. The set will malfunction because the objects are not found in the correct slot of the hash table corresponding to their new value.

Likewise, a HashMap will malfunction if you modify objects used as keys. Similarly with TreeSet and TreeMap. All these data structures can locate objects quickly because each object's value dictates its storage location. The structure becomes wrong if those objects are then modified.

Immutable objects are nicer as set elements and map keys because they avoid this complication.

If you must modify fields that are part of an object's equality, you'll need to temporarily remove it from the set first, and add it again afterwards. Or, use a list as the main container for your objects, and construct a temporary set only when needed.

like image 62
Boann Avatar answered Sep 28 '22 00:09

Boann