10

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 single String.

Is there a better way?

Community
  • 1
  • 1
Nullpoet
  • 10,949
  • 20
  • 48
  • 65

3 Answers3

12

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.

Johannes H.
  • 5,875
  • 1
  • 20
  • 40
5

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.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • While 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. – Johannes H. Nov 06 '13 at 06:02
  • @Johannes What's wrong with Collections.unmodifiableList(list)? – Harald K Nov 06 '13 at 06:12
  • @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()` – Johannes H. Nov 06 '13 at 06:14
  • @Johannes Valid point. :-) – Harald K Nov 06 '13 at 09:24
  • 1
    @Johannes H.: Since the creator of the `List` has the full control over the creation a `Collections.unmodifiableList(Arrays.asList(varargs))` or `Collections.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. – Holger Nov 06 '13 at 10:21
1

You could use Arrays.toString(myArray) as your key.

user949300
  • 15,364
  • 7
  • 35
  • 66
  • That's even more dirty than the approach suggested by the op (which is concatenating the strings instead of using the array) – Johannes H. Nov 06 '13 at 06:07
  • 2
    Why "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. – user949300 Nov 06 '13 at 15:59