0

My HashSet contains multiple 'AccessRequests' with the same HashCode. I only want there to be one instance. I didn't think items with the same HashCode could show up in a HashSet. What am I doing wrong here?

UPDATE: Based on the assumption that HashSet keeps only an item that is not equal to another in a list AND that perhaps my equals/hash methods needed simplification, I have updated my problem. I am still getting MULTIPLE items that evaluate to Equals in my HashSet.

Below are the HashCode and Equals methods from 'AccessRequest'

UPDATE: I updated my hash and equals to only have the necessary fields I need to be "equal"

    @Override
public int hashCode() {
    int hash = 5;
    hash = 79 * hash + Objects.hashCode(this.targets);
    hash = 79 * hash + Objects.hashCode(this.sources);
    hash = 79 * hash + Objects.hashCode(this.destinations);
    hash = 79 * hash + Objects.hashCode(this.services);
    hash = 79 * hash + Objects.hashCode(this.action);
    return hash;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final AccessRequest other = (AccessRequest) obj;
    if (!Objects.equals(this.action, other.action)) {
        return false;
    }
    if (!Objects.equals(this.targets, other.targets)) {
        return false;
    }
    if (!Objects.equals(this.sources, other.sources)) {
        return false;
    }
    if (!Objects.equals(this.destinations, other.destinations)) {
        return false;
    }
    if (!Objects.equals(this.services, other.services)) {
        return false;
    }
    return true;
}

After Creating the AccessRequests, I dump them into a HashSet and Iterate: My HashSet is defined as below:

 Set<AccessRequest> ars = new HashSet();

       ArrayList<AccessRequest> arsAsList = new ArrayList(ars);
        for(int position=0;position<arsAsList.size();position++){
            AccessRequest fixedAR = arsAsList.get(position);
            ArrayList<AccessRequest> comparToList = new ArrayList(ars);
            for(int cPosition=0;cPosition<comparToList.size();cPosition++){
                AccessRequest nextAR = comparToList.get(cPosition);
                if(fixedAR.equals(nextAR)){
                    System.out.println("position= "+position+"  cPosition "+cPosition);
                }
            }
            System.out.println("\n Next AR");
        }

The following is the output:

position= 0  cPosition 0
position= 0  cPosition 5
position= 0  cPosition 6
position= 0  cPosition 14
position= 0  cPosition 24
position= 0  cPosition 32
position= 0  cPosition 39
position= 0  cPosition 40
position= 0  cPosition 43
position= 0  cPosition 77
position= 0  cPosition 96
position= 0  cPosition 97
position= 0  cPosition 99
position= 0  cPosition 109
position= 0  cPosition 111
position= 0  cPosition 115
position= 0  cPosition 173
position= 0  cPosition 182
position= 0  cPosition 187
CoupFlu
  • 311
  • 4
  • 20
  • 3
    yes it is allowed to have hashcode clashes, hashtable also checks equals for the key. – user8426627 Jun 03 '19 at 17:45
  • You're using an awful lot of fields here to calculate the same hash code for unequal objects (4 unequal objects have the same hash code!). Are there actually useful implementations of hashCode for each field? – Andy Turner Jun 03 '19 at 18:13

2 Answers2

5

Sets prevent duplicates based on the equals method (1). From the javadoc (emphasis by me):

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.

If your elements should be equal according to their hash code, then implement the equals method accordingly (e.g. by only comparing the result of calling hashCode). Note that this might not be the best idea as your equals method currently evaluates more properties.

(1): At least the HashSet that you are currently using.

Marvin
  • 13,325
  • 3
  • 51
  • 57
  • Comparing Elements using .equals is also returning duplicate results in my HashSet though – CoupFlu Jun 03 '19 at 19:13
  • Can you show us an [mcve]? Or at least two AccessRequests that are equal to you with _all_ their values, including their hash code? – Marvin Jun 04 '19 at 07:50
0

What you just observed is a hash code collision. Because hash code function maps values from a larger set (e.g. all possible String, there is infinite number of them) into a smaller set (e.g. all possible int, only 2^32 distinct values) there always would be collisions.

That's why data structures that take advantage of hashing always handle hash collisions e.g. by implementing Open Addressing. You can take a look at Hash Tables wiki, Collision resolution section to understand the problem better:

Hash collisions are practically unavoidable when hashing a random subset of a large set of possible keys. For example, if 2,450 keys are hashed into a million buckets, even with a perfectly uniform random distribution, according to the birthday problem there is approximately a 95% chance of at least two of the keys being hashed to the same slot.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111