使用一个字节数组作为Map键

你看到使用一个字节数组作为Map键有任何问题吗? 我也可以通过new String(byte[])来实现new String(byte[])和hash,但是使用byte[]会更直接。

问题是byte[]使用equalshashCode对象标识,所以

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

HashMap不匹配。 我看到三个选项:

  1. 包装在一个String ,但是你必须小心编码问题(你需要确保字节 – >string – >字节给你相同的字节)。
  2. 使用List<Byte> (在内存中可能是昂贵的)。
  3. 做你自己的包装类,写hashCodeequals使用字节数组的内容。

没关系,只要你的键只需要引用相等就可以了 – 数组不会以你想要的方式实现“值相等”。 例如:

 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()); 

打印如下:

 false 1671711 11394033 

(实际的数字是不相关的,它们不同的事实很重要。)

假设你真的想要平等,我build议你创build自己的包装,其中包含一个byte[] ,并适当地实现相等和哈希代码生成:

 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); } } 

请注意,如果在使用ByteArrayWrapper后将字节数组中的值更改为HashMap (etc)中的键,则在再次查找关键字时会遇到问题…您可以在ByteArrayWrapper构造函数中获取数据的副本如果你想的话,但是如果你知道你不会改变字节数组的内容,显然这将浪费性能。

编辑:正如在评论中提到的,你也可以使用ByteBuffer这个(特别是,它的ByteBuffer#wrap(byte[])方法)。 考虑到ByteBuffer具备的所有额外function,但是这是一个选项,我不知道它是否正确。

我们可以使用ByteBuffer(这基本上是比较器的byte []包装器)

 HashMap<ByteBuffer, byte[]> kvs = new HashMap<ByteBuffer, byte[]>(); byte[] k1 = new byte[]{1,2 ,3}; byte[] k2 = new byte[]{1,2 ,3}; byte[] val = new byte[]{12,23,43,4}; kvs.put(ByteBuffer.wrap(k1), val); System.out.println(kvs.containsKey(ByteBuffer.wrap(k2))); 

将打印

 true 

你可以使用java.math.BigInteger 。 它有一个BigInteger(byte[] val)构造函数。 这是一个引用types,所以可以用作散列表的键。 .equals().hashCode()定义为相应的整数,这意味着BigInteger与byte []数组具有一致的equals语义。

我很惊讶,答案并没有指出最简单的select。

是的,使用HashMap是不可能的,但是没有人会阻止你使用SortedMap作为替代。 唯一的事情就是写一个需要比较数组的比较器。 它不像HashMap那么高性能,但是如果你想要一个简单的select,那么你可以去(如果你想隐藏实现,你可以用MapreplaceSortedMap):

  private SortedMap<int[], String> testMap = new TreeMap<>(new ArrayComparator()); private class ArrayComparator implements Comparator<int[]> { @Override public int compare(int[] o1, int[] o2) { int result = 0; int maxLength = Math.max(o1.length, o2.length); for (int index = 0; index < maxLength; index++) { int o1Value = index < o1.length ? o1[index] : 0; int o2Value = index < o2.length ? o2[index] : 0; int cmp = Integer.compare(o1Value, o2Value); if (cmp != 0) { result = cmp; break; } } return result; } } 

这个实现可以针对其他数组进行调整,唯一需要注意的是相等的数组(等于等于成员的长度)必须返回0,并且您有一个确定的顺序

我相信Java中的数组并不一定直观地实现hashCode()equals(Object)方法。 也就是说,两个相同的字节数组将不一定共享相同的哈希码,并且它们不一定要求相等。 没有这两个特性,你的HashMap就会出乎意料的performance。

因此,我build议不要在HashMap 使用byte[]作为键。

你应该使用创build一个类似的ByteArrKey和重载hashcode和等同的方法,记住它们之间的契约。

这会给你更大的灵活性,因为你可以跳过附加在字节数组末尾的0个条目,特别是如果只复制另一个字节缓冲区的一部分。

这样你将决定两个对象应该如何相等。

我看到了问题,因为你应该使用Arrays.equals和Array.hashCode来代替默认的数组实现

Arrays.toString(字节)

您还可以使用Base32或Base64将字节[]转换为“安全”string,例如:

 byte[] keyValue = new byte[] {…}; String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue); 

当然上面有很多变种,比如:

 String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue);