tags:

views:

2908

answers:

5

See topic - do you see any problem with this? I could also do new String(byte[]) and hash by String but it is more straightforward to use byte[]

+2  A: 

I believe that arrays in Java do not necessarily implement the hashCode() and equals(Object) methods intuitively. That is, two identical byte arrays will not necessarily share the same hash code and they will not necessarily claim to be equal. Without these two traits, your HashMap will behave unexpectedly.

Therefore, I recommend against using byte[] as keys in a HashMap.

Adam Paynter
s/not necessarily/not/
Michael Borgwardt
I suppose my wording was a bit off. I was accounting for the situation where THE SAME byte array is being used both for insertion into the hash map AND for retrieval from the hash map. In that case, "both" byte arrays are identical AND share the same hash code.
Adam Paynter
+17  A: 

It's okay so long as you only want reference equality for your key - arrays don't implement "value equality" in the way that you'd probably want. For example:

byte[] array1 = new byte[1];
byte[] array2 = new byte[1];

System.out.println(array1.equals(array2));
System.out.println(array1.hashCode());
System.out.println(array2.hashCode());

prints something like:

false
1671711
11394033

(The actual numbers are irrelevant; the fact that they're different is important.)

Assuming you actually want equality, I suggest you create your own wrapper which contains a byte[] and implements equality and hash code generation appropriately:

public final class ByteArrayWrapper
{
    private final byte[] data;

    public ByteArrayWrapper(byte[] data)
    {
        if (data == null)
        {
            throw new NullPointerException();
        }
        this.data = data;
    }

    @Override
    public boolean equals(Object other)
    {
        if (!(other instanceof ByteArrayWrapper))
        {
            return false;
        }
        return Arrays.equals(data, ((ByteArrayWrapper)other).data);
    }

    @Override
    public int hashCode()
    {
        return Arrays.hashCode(data);
    }
}

Note that if you change the values within the byte array after using the ByteArrayWrapper, as a key in a HashMap (etc) you'll have problems looking up the key again... you could take a copy of the data in the ByteArrayWrapper constructor if you want, but obviously that will be a waste of performance if you know you won't be changing the contents of the byte array.

Jon Skeet
@dfa: The "instanceof" test handles the null case.
Jon Skeet
A couple of other things you could add to the wrapper implementation:1. Take a copy of the byte[] on construction therefore guaranteeing that the object is immutable, meaning there's no danger your key's hash code will change over time.2. Pre-compute and store the hash code once (assuming speed is more important than storage overhead).
Adamski
@Adamski: I mention the possibility of copying at the end of the answer. In some cases it's the right thing to do, but not in others. I'd probably want to make it an option (possibly static methods instead of constructors - copyOf and wrapperAround). Note that *without* copying, you can change the underlying array until you first take the hash and check for equality, which could be useful in some situations.
Jon Skeet
Oops - Sorry Jon; I missed that part of your response.
Adamski
A: 

I see problems since you should use Arrays.equals and Array.hashCode, in place of default array implementations

dfa
And how would you make the HashMap use those?
Michael Borgwardt
see Jon Skeet's answer (a byte array wrapper)
dfa
+5  A: 

The problem is that byte[] uses object identity for equals and hashCode, so that

byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}

will not match in a HashMap. I see three options:

  1. Wrapping in a String, but then you have to be careful about encoding issues (you need to make certain that the byte -> String -> byte gives you the same bytes).
  2. Use List<Byte> (can be expensive in memory).
  3. Do your own wrapping class, writing hashCode and equals to use the contents of the byte array.
Kathy Van Stone
A: 

Arrays.toString(bytes)

df