Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a byte array as Map key

Do you see any problem with using a byte array as Map key? I could also do new String(byte[]) and hash by String but it is more straightforward to use byte[].

like image 426
shikhar Avatar asked Jun 29 '09 13:06

shikhar


People also ask

Can we use array as key in Map?

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.

Can we use array as key in HashMap?

You cannot do it this way. Both t and a will have different hashCode() values because the the java. lang. Array.

Can I use Map as a key?

You can't use a Map as a key but you can use it as a value. To explain further, since you are likely to be adding to the HashMap the key will not remain constant and thus will no longer serve it's purpose as a key. You can have a Map as key but as you pointed out since a Map is (typically) mutable the key changes.

Can HashMap have array as value?

In a HashMap, keys and values can be added using the HashMap. put() method. We can also convert two arrays containing keys and values into a HashMap with respective keys and values.


2 Answers

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.

EDIT: As mentioned in the comments, you could also use ByteBuffer for this (in particular, its ByteBuffer#wrap(byte[]) method). I don't know whether it's really the right thing, given all the extra abilities that ByteBuffers have which you don't need, but it's an option.

like image 77
Jon Skeet Avatar answered Oct 01 '22 01:10

Jon Skeet


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.
like image 30
Kathy Van Stone Avatar answered Oct 01 '22 01:10

Kathy Van Stone