使用一个字节数组作为Map键
你看到使用一个字节数组作为Map键有任何问题吗? 我也可以通过new String(byte[])
来实现new String(byte[])
和hash,但是使用byte[]
会更直接。
问题是byte[]
使用equals
和hashCode
对象标识,所以
byte[] b1 = {1, 2, 3} byte[] b2 = {1, 2, 3}
在HashMap
不匹配。 我看到三个选项:
- 包装在一个
String
,但是你必须小心编码问题(你需要确保字节 – >string – >字节给你相同的字节)。 - 使用
List<Byte>
(在内存中可能是昂贵的)。 - 做你自己的包装类,写
hashCode
,equals
使用字节数组的内容。
没关系,只要你的键只需要引用相等就可以了 – 数组不会以你想要的方式实现“值相等”。 例如:
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);