Search code examples
javadictionaryhashmaptreemapcomparable

Java use byte[] as key in a Map


I am having a little bit of struggle with Java maps. I want to have a map which takes a byte[] as a key and returns a custom object as the value.

First I tried using hash maps, but because I used to different arrays, but with the same values, it didn't work. Also using Arrays.hashCode() isn't possible because multible arrays with different values, would have the same hash.

Now I'm trying it with a TreeMap instead of a HashMap, but now I don't know how to solve the

Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(TreeMap.java:1294)
    at java.util.TreeMap.put(TreeMap.java:538)

Exception which gets thrown when I try to add an Object.

Has somebody an idea how I could solve this exception? Or more specific, how and which Comparator should I provide to the TreeMap?


Solution

  • Ok, introduction material to Java here.

    There are two things at play:

    1. Arrays are identity-based, not value-based (see this answer for a bit more details; I couldn't find any formal specification for this, though).

    Hence, the following returns false:

    byte[] a = {1};
    byte[] b = {1};
    System.out.println(a.equals(b));
    
    1. Arrays do not implement Comparable (it would make no sense for them). In order to put anything into a TreeMap, you must either provide a Comparator for it (using this constructor), or it must implement Comparable.

    EDIT: If you indeed have some binary data which you want to use as a key (which is a rather strange case), you can:

    1. Use Java NIO's ByteBuffer and its ByteBuffer.wrap(byte[]) method (drawback: this class is mutable but so was byte[] in your original version).

    2. Create a custom (preferably immutable) wrapper over byte[], and use this class as a key for your HashMap/TreeMap (though, if you do not need sorting, do not use TreeMap for performance reasons).

    Stub of a sample custom wrapper:

    final class ByteWrapper {
    
        private final byte[] bytes;
    
        public ByteWrapper(byte[] bytes) {
            this.bytes = bytes;
        }
    
        @Override
        public boolean equals(Object o) {
            // type checks here
            ByteWrapper that = (ByteWrapper) o;
            return Arrays.equals(bytes, that.bytes);
        }
    
        @Override
        public int hashCode() {
            return Arrays.hashCode(bytes);
        }
    }