I have a requirement in which I need to map multiple determinants to values.
Each set of determinants in a given job execution is guaranteed to be unique. The value to be determined doesn't have to be unique but it probably is.
Depending on the input to the job execution, this could be either one key, or the combination of two keys, or the combination of
n
keys that will be mapped to a single value. In practice thisn
will probably be limited to no more than 5, although it is possible it could exceed that.Each job execution will have a set number of determinants for all inputs (I.e., all inputs will have either 2 determinants, 3 determinants, or n determinants, and will not have a mix).
One key example: foo --> bar
Two keys: foo, bar --> baz
Three keys: foo, bar, baz --> hai
Prior to this, the requirement was that I would only ever map two values to another value. I created an immutable Key class with two member variables and the appropriate override of equals
and hashCode
.
public class Key {
String determinant0;
String determinant1;
public Key(String d0, d1) {
determinant0 = d0;
determinant1 = d1;
}
// ..
}
However, now that I may be dealing with n number of values, I want to take a look at using a list as the key.
Map<List, String> map = new HashMap<List, String>();
map.put(Arrays.asList("foo", "bar", "baz"), "hai");
String determined = map.get(Arrays.AsList("foo","bar","baz"));
assert (determined.equals("hai"));
This question reminds me that it is bad to use a mutable object (like a List) as a key in a map. However, in my application, the key is only set once and is never altered. Here is an alternative from this question that forces it to be immutable:
HashMap<List<String>, String> map;
map.put(
// unmodifiable so key cannot change hash code
Collections.unmodifiableList(Arrays.asList("foo", "bar", "baz")),
"hai"
);
In addition, I could always make a class like the following to prevent mutations on the list:
public class Key {
List<String> determinants;
public Key(List<String> determinants) {
this.determinants = determinants
}
@Override
public boolean equals(Object obj) {
//...
}
@Override
public int hashCode() {
//...
}
}
Key key = new Key(Arrays.asList("foo","bar","baz"));
Using a plain array as the key won't work, because an array's equal method only checks for identity:
Map<String[], String> map = new HashMap<String[], String>();
String[] key = new String[]{"foo", "bar", "baz"}
map.put(key, "hai");
System.out.println(map.get(key)); // null
That could be fixed by the following:
public class Key {
String[] determinants;
public Key(String... determinants) {
this.determinants = determinants;
}
@Override
public boolean equals(Object obj) {
//...
}
@Override
public int hashCode() {
//...
}
}
How about concatting all the determinants together in a string?
public class Key {
String hash = "";
public Key(String... determinants) {
for (String determinant : determinants) {
hash += determinant + "_";
}
}
@Override
public boolean equals(Object obj) {
//...
}
@Override
public int hashCode() {
//...
}
}
Which one of these solutions (or another one that I did not propose) is the best suited for these requirements?