题
您发现使用字节数组作为 Map 键有什么问题吗?我也可以做 new String(byte[])
并散列 String
但使用起来更简单 byte[]
.
解决方案
问题是 byte[]
使用对象标识 equals
和 hashCode
, , 以便
byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}
将不匹配 HashMap
. 。我看到三个选项:
- 包裹在一个
String
, ,但是你必须小心编码问题(你需要确保 byte -> String -> byte 给你相同的字节)。 - 使用
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
(实际数字无关紧要;它们不同这一事实很重要。)
假设你 实际上 想要平等,我建议你创建自己的包装器,其中包含 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
, ,作为 a 中的键 HashMap
(等等)你再次查找密钥时会遇到问题......您可以复制其中的数据 ByteArrayWrapper
如果您愿意的话,可以使用构造函数,但如果您知道的话,显然这会浪费性能 惯于 更改字节数组的内容。
编辑:正如评论中提到的,您还可以使用 ByteBuffer
为此(特别是其 ByteBuffer#wrap(byte[])
方法)。我不知道这是否真的是正确的事情,考虑到所有额外的能力 ByteBuffer
有你不需要的,但它是一个选择。
我们可以使用字节缓冲区为这个(这基本上是字节[]包装用比较器)
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)
构造。这是一个引用类型,所以可以用来作为哈希表中的关键。和.equals()
和.hashCode()
被定义为用于各个整数,这意味着具有BigInteger的一致等于语义byte []数组。
我很惊讶的是,答案是没有指向了最简单的选择。
是,它不可能使用HashMap,但没有人防止您使用一个SortedMap作为替代。唯一的办法就是先写这需要比较的数组中的比较。这是不是一个HashMap的高性能,但如果你想要一个简单的选择,在这里你去(你可以用地图替换为SortedMap如果你想隐藏实现):
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,并且您有一个determistic顺序
我相信,在Java数组不一定落实hashCode()
和equals(Object)
方法直观。也就是说,两个相同的字节数组不一定会共享相同的散列码,他们将不一定要求相等。如果没有这两个特点,你的HashMap的会表现异常。
因此,我建议的针对强>使用byte[]
作为HashMap密钥。
您应该使用创建一个类的财产以后像ByteArrKey和过载哈希码和平等的方式,记住他们之间的合同。
这会给你更大的灵活性,可跳过在字节数组的末尾追加0项,特别是如果只复制一些部分形式的其它字节的缓冲区。
这样,你将决定这两个对象应该如何平等的。
我看到的问题,因为你应该使用满足Arrays.equals和Array.hashCode,代替默认数组实现
Arrays.toString(字节)
您也可以在字节[]转换为使用Base32或Base64,“安全”的字符串例如:
byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);
当然有上述的许多变体,如:
String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue);
下面(字节[],字节[]);
请注意:在地图上的顺序是不相关的用该方法
SortedMap<byte[], String> testMap = new TreeMap<>(new ArrayComparator());
static class ArrayComparator implements Comparator<byte[]> {
@Override
public int compare(byte[] byteArray1, byte[] byteArray2) {
int result = 0;
boolean areEquals = Arrays.equals(byteArray1, byteArray2);
if (!areEquals) {
result = -1;
}
return result;
}
}