Can an array be used as a HashMap key?

45,243

Solution 1

It will have to be the same object. A HashMap compares keys using equals() and two arrays in Java are equal only if they are the same object.

If you want value equality, then write your own container class that wraps a String[] and provides the appropriate semantics for equals() and hashCode(). In this case, it would be best to make the container immutable, as changing the hash code for an object plays havoc with the hash-based container classes.

EDIT

As others have pointed out, List<String> has the semantics you seem to want for a container object. So you could do something like this:

HashMap<List<String>, String> pathMap;

pathMap.put(
    // unmodifiable so key cannot change hash code
    Collections.unmodifiableList(Arrays.asList("korey", "docs")),
    "/home/korey/docs"
);

// later:
String dir = pathMap.get(Arrays.asList("korey", "docs"));

Solution 2

No, but you can use List<String> which will work as you expect!

Solution 3

Arrays in Java use Object's hashCode() and don't override it (the same thing with equals() and toString()). So no, you cannot shouldn't use arrays as a hashmap key.

Solution 4

You cannot use a plain Java Array as a key in a HashMap. (Well you can, but it won't work as expected.)

But you could write a wrapper class that has a reference to the Array and that also overrides hashCode() and equals().

Solution 5

Ted Hopp is right it will have to be same object.

For information see this example:

public static void main(String[] args) {
    HashMap<String[], String> pathMap;
    pathMap = new HashMap<String[], String>();
    String[] data = new String[]{"korey", "docs"};
    pathMap.put(data, "/home/korey/docs");
    String path = pathMap.get(data);
    System.out.println(path);
}

When you run the above code, it will print "docs".

Share:
45,243
Korey Hinton
Author by

Korey Hinton

iOS Developer Web-Site, Blog, iOS Blog, GitHub

Updated on July 09, 2022

Comments

  • Korey Hinton
    Korey Hinton almost 2 years

    If a HashMap's key is a String[] array:

    HashMap<String[], String> pathMap;
    

    Can you access the map by using a newly created String[] array, or does it have to be the same String[] object?

    pathMap = new HashMap<>(new String[]{"korey", "docs"}, "/home/korey/docs");
    String path = pathMap.get(new String[]{"korey", "docs"});
    
  • pvorb
    pvorb almost 11 years
    I think it also uses hashCode() to determine the hash of an object.
  • Lucas
    Lucas almost 11 years
    You can use them as a key, it will just use whatever Object does for its hashCode... Not what he wants, but nothing stopping you from doing it.
  • Ted Hopp
    Ted Hopp almost 11 years
    @pvorb - Indeed. And two arrays are unlikely to have the same hash code. However, this is not a requirement for any Java implementation. In any case, two references with the same hashCode() are then compared using equals() to determine if they are the same key.
  • Steve Kuo
    Steve Kuo almost 11 years
    No need write a new array wrapper class, one already exists - ArrayList
  • Ted Hopp
    Ted Hopp almost 11 years
    This works with a caveat. If you're going to use a List<String> as a key in a hash-based collection, the list should be unmodifiable. If the hash code for an object changes while the object is being used as a key in a hash-based collection, the collection generally breaks.
  • Steve Kuo
    Steve Kuo almost 11 years
    List is an interface, and there no guarantee that an implementation properly overrides equals and hashCode
  • pvorb
    pvorb almost 11 years
    @SteveKuo Yes, indeed. But maybe you want to write your own, since ArrayList is mutable and the array underneath can be replaced internally without you noticing it.
  • Ted Hopp
    Ted Hopp almost 11 years
    @SteveKuo - Yes there is. The documentation for List requires any implementation to use a specific semantics for equals() and hashCode(). The required semantics matches what OP seems to want.
  • Ted Hopp
    Ted Hopp almost 11 years
    @pvorb - One can always use Collections.unmodifiableList(someList) to turn a List into an immutable object.
  • Korey Hinton
    Korey Hinton almost 11 years
    @TedHopp I decided to use your suggestion to make a container class. Thanks!
  • Ted Hopp
    Ted Hopp almost 11 years
    @KoreyHinton - You're welcome. :) Before doing that, though, check whether List does what you need. See my edited answer.
  • Korey Hinton
    Korey Hinton almost 11 years
    @TedHopp Cool I just now saw your edit, thanks for showing a working example!
  • Mark Bennett
    Mark Bennett about 10 years
    This all sounded very promising, but when I try to do containsKey I get a ClassCastException: java.util.Collections$UnmodifiableList cannot be cast to java.lang.Comparable, still researching...
  • Ted Hopp
    Ted Hopp about 10 years
    @MarkBennett - Weird. AFAIK HashMap.containsKey does not require keys to implement Comparable. It only relies on hashCode() and equals(). Something else must be going on in your code.
  • Theodore Murdock
    Theodore Murdock over 7 years
    Note that the collection returned by Collections.unmodifiableList(List) is not immutable--if the underlying list is later modified, the hashcode of the unmodifiable list wrapping it will change. So the call to Collections.unmodifiableList protects the keys from modification by, for example, a malicious or misguided consumer iterating the keyset and modifying keys, but if the underlying list used as a key could later change, defensive copying is also necessary.
  • Ted Hopp
    Ted Hopp over 7 years
    @TheodoreMurdock - Good point. The usual idiom for using Collections.unmodifiableList(List) is to not keep a reference to the argument (as in my sample code), so this usually isn't a problem. It's only when the argument is separately modifiable (through a separate reference) that things can get messed up.
  • Alex
    Alex almost 7 years
    @TedHopp: an unmodifiableList is not an immutable one.You still can modify the source list directly.
  • Ted Hopp
    Ted Hopp almost 7 years
    @Alex - Yes, you are correct. This was pointed out in this comment to my own answer above. See also my response to that comment.
  • Florian F
    Florian F over 5 years
    The question would be then: Is there a suitable container class that will wrap an array and provide the needed hasCode() and equals()? It looks like a very generic thing.
  • Ted Hopp
    Ted Hopp over 5 years
    @FlorianF - You mean for an array of mutable objects? I think an identity hash is the only sensible approach in that case. If the hash code for key objects can change dynamically, a hash-based data structure is unsuitable right from the start.
  • Peng
    Peng over 3 years
    Indeed, the AbstractList class which is the superclass all list implementations inherit from overrides both hashCode() and equals() methods, so that the element-wise comparison, rather than reference comparison, is reflected.
  • Peng
    Peng over 3 years
    The problem with this approach is that it cannot deal with hash collision when 2 string[] has the same hashCode under Arrays.hashCode().