Reputation: 738
I have a Multimap
to store some data with composite key (data type, data id).
Here is my code:
public class Data {
private final String type;
private final Integer id;
private final String information;
public Data(String type, Integer id, String information) {
this.type = type;
this.id = id;
this.information = information;
}
@Override
public String toString() {
return "Type: " + type + ", ID: " + id + ", Info: " + information;
}
}
public class Test {
private static Multimap<Key, Data> data = HashMultimap.create();
static class Key {
private final String type;
private final Integer id;
public Key(String type, Integer id) {
this.type = type;
this.id = id;
}
@Override
public int hashCode() {
//System.out.println("in hashcode");
return Objects.hashCode(type);
}
@Override
public boolean equals(Object obj) {
//System.out.println("in equals");
if (obj == null) {
return false;
}
if (!(obj instanceof Key)) {
return false;
}
Key other = (Key) obj;
if (other.type.equals(type)) {
if (other.id == null) { // Return true for null ID Data
return true;
} else if (other.id.equals(id)) {
return true;
}
}
return false;
}
}
private static void addData(String type, Integer id, String information) {
data.put(new Key(type, id), new Data(type, id, information));
}
private static void printData(String type, Integer id) {
System.out.println("\n===============");
System.out.println("Filtered Data for: Type => " + type + ", ID => " + id);
for (Data d : data.get(new Key(type, id))) {
System.out.println(d);
}
}
public static void main(String[] args) {
addData("confidential", 11, "Some Confidential Data!");
addData("private", 21, "Some Private Data!");
addData("government", 13, "Some Govt. Data!");
addData("public", null, "Some public data!");
addData("public", 26, "Another public data!");
addData("public", 4, "More public information!");
addData("unspecified", null, "Lorem ipsum dolor sit amet!");
addData("unspecified", 15, "consectetur adipiscing elit.");
addData("unspecified", 25, "Integer sed velit vel.");
addData("unspecified", null, "In vitae velit consequat");
printData("unspecified", 25);
printData("public", 26);
}
}
Now, I want to filter this data by Data::type
& Data::id
, including data whose Data::type
matches but Data::id
is null
.
Expected Output:
===============
Filtered Data for: Type => unspecified, ID => 25
Type: unspecified, ID: null, Info: In vitae velit consequat
Type: unspecified, ID: null, Info: Lorem ipsum dolor sit amet!
Type: unspecified, ID: 25, Info: Integer sed velit vel.
===============
Filtered Data for: Type => public, ID => 26
Type: public, ID: null, Info: Some public data!
Type: public, ID: 26, Info: Another public data!
Actual Output
===============
Filtered Data for: Type => unspecified, ID => 25
Type: unspecified, ID: null, Info: In vitae velit consequat
Type: unspecified, ID: 15, Info: consectetur adipiscing elit.
Type: unspecified, ID: null, Info: Lorem ipsum dolor sit amet!
Type: unspecified, ID: 25, Info: Integer sed velit vel.
===============
Filtered Data for: Type => public, ID => 26
Type: public, ID: 4, Info: More public information!
Type: public, ID: null, Info: Some public data!
Type: public, ID: 26, Info: Another public data!
For this purpose, I've implemented hashCode()
& equals()
methods in Key
class and returned true
when other data object id
is null
.
Questions:
Am I using the correct approach or this can be implemented using simple Map
?
Why hashCode
& equals
methods are called only once, when I call Multimap::get()
method? (According to me, each key in map should've matched with the specified key!)
If it's the correct approach, how to implement equals
method to get expected result?
Upvotes: 1
Views: 357
Reputation: 3666
In order for the hash map to work properly, the equals implementation must satisfy the constraint that if a.equals(b)
and b.equals(c)
then it must be true that a.equals(c)
. If you break that constraint then the map will behave unpredictably. This property is called transitivity. See the full list of constraints on equals implementations here.
To do what you want you must do two lookups in the printData
method, one with new Key(type, id)
and one with new Key(type, null)
. There is no shortcut with clever equals implementations.
To answer the question, why are you seeing hashCode
and equals
called only once when you call get()
? This is another consequence of that constraint on equals
. The map has already determined that Key(x, 15)
and Key(x, null)
are the same, according to your equals
implementation. This was done when you inserted the data. So on lookup it only has to compare against one of them and then it assumes the other will also match.
Upvotes: 2