Using string tuples as key for HashMap
Solution 1
Arrays in Java don't provide hashCode()
and equals(Object)
methods, so they aren't appropriate as map keys.
What you could use instead is Arrays.asList(string1, string1, etc)
which would give you an immutable List
, which all the methods needed for a Map
's key.
Solution 2
HashMaps use Object.hashCode()
to create the hash. This, by default, uses a hash of the object that is unique for each instance - but doesn't look into any contents.
You migth want to create a tuple that overrides hashCode()
and, in addition to that, is immutable once created:
public class Tuple<T> {
private final T[] contents;
public Tuple (T[] contents) {
if (contents.length != 2)
throw new IllegalArgumentException();
this.contents = contents;
}
public T[] getContents () {
return this.contents.clone();
}
@Override
public int hashCode () {
return Arrays.deepHashCode(this.contents);
}
@Override
public boolean equals (Object other) {
return Arrays.deepEquals(this.contents, other.getContents());
}
@Override
public String toString () {
return Arrays.deepToString(this.contents);
}
}
[Edit]: Note that, if mutable objects are used instead of strings, the getter must perform a deep copy, not just a simple clone()
to ensure immutability.
Solution 3
You could use Arrays.toString(myArray) as your key.
Nullpoet
Updated on June 09, 2022Comments
-
Nullpoet almost 2 years
I need Java equivalent for following Python:
In [1]: d = {} In [2]: k = ("x","2") In [3]: d[k] = 1 In [4]: print d[("x","y")]
1
Python has tuples which are hashable. I tried following in Java unsuccessfully:
Map<String[], Integer> d = new HashMap<String[], Integer>(); String[] k = new String[]{"x", "y"}; d.put(k, 1); System.out.println(d.get(k)); System.out.println(d.get(new String[]{"x", "y"}));
It outputs:
1 null
This means reference to
String[]
is getting hashed instead of the value.An inefficient way I can think of is concatenating elements from
String[]
into a singleString
.Is there a better way?
-
Johannes H. over 10 yearsWhile possible, I'd recommend to create an immutable list out of that. Unfortunately Java doesn't provide one, but Guava for example does. Of course you could always write one on your own, too.
-
user949300 over 10 yearsWhy clone every call to hashcode()?
-
Johannes H. over 10 years@user949300: I don't. I only clone the content array in the getter to make sure no changes are made to it, so the tuple is immutable.
-
Johannes H. over 10 yearsThat's even more dirty than the approach suggested by the op (which is concatenating the strings instead of using the array)
-
Johannes H. over 10 yearsNot sure BTW how adding up the hashcodes of both contents behaves. Maybe a more elaborate
hashCode
function is needed here. Any comments on this? -
Harald K over 10 years@Johannes What's wrong with Collections.unmodifiableList(list)?
-
Johannes H. over 10 years@haraldK: See the description of
ImmutableList
in Guava:Unlike Collections#unmodifiableList, which is a view of a separate collection that can still change, an instance of ImmutableList contains its own private data and will never change.
So, in short terms, the difference is: if you still got an reference to the original list, you can still modify it if you used.unmodifiableList()
-
Bimalesh Jha over 10 yearsshould override
equals()
also and make thatT[]
instance field asprivate final
for completeness :) -
Johannes H. over 10 years@BimaleshJha: agreed, edited. Hope I didn't make any boo-boos in that quick and dirty edit ;)
-
Harald K over 10 years@Johannes Valid point. :-)
-
Holger over 10 years@Johannes H.: Since the creator of the
List
has the full control over the creation aCollections.unmodifiableList(Arrays.asList(varargs))
orCollections.unmodifiableList(Arrays.asList(array.clone()))
is sufficient. There’s no reason to replace this code adding 3rd party library dependencies as this code is safe and the 3rd party library still wouldn’t prevent other code from using mutable lists. -
user949300 over 10 yearsWhy "dirtier"? First, there's little coding, and you end up with an immutable String. And the String is bounded by [] and delimited by commas, so it is unlikely to accidentally match. e.g. OP's scheme doesn't work for "dog","ate" vs. "do","gate". Mine does.