Reputation: 1798
I have implemented my multiple key class as follows:
public class ProbabilityIndex {
private int trueLabel;
private int classifiedLabel;
private int classifierIndex;
public ProbabilityIndex(int trueLabel, int classifiedLabel, int classifierIndex) {
this.trueLabel = trueLabel;
this.classifiedLabel = classifiedLabel;
this.classifierIndex = classifierIndex;
}
@Override
public boolean equals(Object obj) {
if ( !obj instanceof ProbabilityIndex)
return false;
if (obj == this)
return true;
ProbabilityIndex rhs = (ProbabilityIndex) obj;
return new EqualsBuilder().
append(trueLabel, rhs.trueLabel).
append(classifiedLabel, rhs.classifiedLabel).
append(classifierIndex, rhs.classifierIndex).
isEquals();
}
@Override
public int hashCode() {
int hashCode = new HashCodeBuilder(17, 31).
append(trueLabel).
append(classifiedLabel).
append(classifierIndex).
toHashCode();
return hashCode;
}
}
Notice that trueLabel
, classifiedLabel
and classifierIndex
are all either 0 or 1.
Then, I use my key as follows:
ProbabilityIndex key = new ProbabilityIndex(trueLabel, classifiedLabel, classifierIndex);
probabilities.put(key, new Double(value));
where probabilities
is declared as follows:
HashMap<ProbabilityIndex, Double> probabilities;
However, different combinations of trueLabel
, classifiedLabel
and classifierIndex
write the tuple in the same position in probabilities
, thus overwriting existing tuples.
How can I overcome this issue?
Minimal test case:
HashMap<ProbabilityIndex, Double> map = new HashMap<ProbabilityIndex, Double>();
map.put(new ProbabilityIndex(0, 0, 0), new Double(0.1));
map.put(new ProbabilityIndex(0, 0, 1), new Double(0.2));
map.put(new ProbabilityIndex(0, 1, 0), new Double(0.1));
map.put(new ProbabilityIndex(0, 1, 1), new Double(0.2));
map.put(new ProbabilityIndex(1, 0, 0), new Double(0.1));
This inserts 4 tuples instead of 5.
Upvotes: 1
Views: 66
Reputation: 200148
I can only tell you that the hashtable will never overwrite objects with the same hash code (a hash collision); it will just be less efficient in their retrieval.
The only way to have your entries incorrectly overwritten is by providing an equals
method for the key which returns true
for different keys.
A bit of further advice not directly related to your problem: if all you have is three two-state variables, then the complete value set for the class has cardinality of just 8. Instead of the complicated hash code builder you use, you could just construct the hash code with three bits, each representing one variable. That would plainly ensure that each state of your object has a distinct hash code.
I have verified your code with the following implementations of hashCode()
and equals()
(I had to change equals
to make your example truly self-contained):
@Override public boolean equals(Object obj) {
if (!(obj instanceof ProbabilityIndex)) return false;
if (obj == this) return true;
ProbabilityIndex rhs = (ProbabilityIndex) obj;
return this.trueLabel == rhs.trueLabel
&& this.classifiedLabel == rhs.classifiedLabel
&& this.classifierIndex == rhs.classifierIndex;
}
@Override public int hashCode() {
return trueLabel | (classifiedLabel << 1) | (classifierIndex << 2);
}
Your test code resulted in a map with five entries.
As a final note, you don't even need a hashtable if its maximum size will be only 8. A plain array of size 8, indexed by the above hash code, would be enough.
Upvotes: 3