Java 使用字节数组作为映射键
您认为使用字节数组作为映射键有什么问题吗?我也可以做Java 使用字节数组作为映射键,java,hashmap,bytearray,Java,Hashmap,Bytearray,您认为使用字节数组作为映射键有什么问题吗?我也可以做新字符串(byte[])和按字符串进行散列,但是使用byte[]更简单,我相信Java中的数组不一定直观地实现hashCode()和equals(Object)方法。也就是说,两个相同的字节数组不一定共享相同的哈希代码,也不一定声称相等。如果没有这两个特性,HashMap的行为将出人意料 因此,我建议不要使用byte[]作为HashMap中的键。只要您只希望键的引用相等,就可以了-数组不会以您可能希望的方式实现“值相等”。例如: byte[]
新字符串(byte[])
和按字符串进行散列,但是使用byte[]
更简单,我相信Java中的数组不一定直观地实现hashCode()
和equals(Object)
方法。也就是说,两个相同的字节数组不一定共享相同的哈希代码,也不一定声称相等。如果没有这两个特性,HashMap的行为将出人意料
因此,我建议不要使用byte[]
作为HashMap中的键。只要您只希望键的引用相等,就可以了-数组不会以您可能希望的方式实现“值相等”。例如:
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());
byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);
打印如下内容:
false
1671711
11394033
(实际数字无关紧要;它们不同的事实很重要。)
假设您确实想要相等,我建议您创建自己的包装器,其中包含字节[]
,并适当地实现相等和哈希代码生成:
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);
}
}
请注意,如果在使用ByteArrayRapper
作为HashMap
(等)中的键后更改字节数组中的值,则再次查找该键时会遇到问题。。。如果需要,您可以在ByteArrayRapper
构造函数中复制数据,但是如果您知道不更改字节数组的内容,那么这显然是对性能的浪费
编辑:如评论中所述,您也可以使用ByteBuffer
进行编辑(尤其是其方法)。我不知道这是否真的是正确的,因为ByteBuffer具有所有您不需要的额外功能,但这是一个选项。我看到了问题,因为您应该使用Array.equals和Array.hashCode来代替默认的数组实现。问题在于byte[]
将对象标识用于equals
和hashCode
,以便
byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}
在哈希映射中不匹配。我认为有三种选择:
在字符串中包装,但是您必须小心编码问题(您需要确保byte->String->byte为您提供相同的字节)
使用列表
(在内存中可能很昂贵)
编写自己的包装类,编写hashCode
和equals
以使用字节数组的内容
Arrays.toString(bytes)您可以使用java.math.biginger
。它有一个biginger(byte[]val)
构造函数。它是一个引用类型,因此可以用作哈希表的键。和.equals()
和.hashCode()
分别定义为相应的整数,这意味着BigInteger与byte[]数组具有一致的equals语义。我们可以使用ByteBuffer实现这一点(这基本上是带有比较器的byte[]包装器)
您还可以使用Base32或Base64将字节[]转换为“安全”字符串,例如:
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());
byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);
当然,上面有许多变体,例如:
String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue);
您应该使用创建一个类,比如ByteArrKey和重载hashcode以及equal方法,记住它们之间的约定
这将为您提供更大的灵活性,因为您可以跳过附加在字节数组末尾的0个条目,特别是当您仅复制另一个字节缓冲区中的某些部分时
这样你就可以决定两个物体应该如何相等。我很惊讶,答案没有指出最简单的选择
是的,不可能使用HashMap,但是没有人阻止您使用SortedMap作为替代。唯一需要做的就是编写一个比较器来比较数组。它的性能不如HashMap,但是如果您想要一个简单的替代方案,那么就来这里(如果您想隐藏实现,可以用Map替换SortedMap):
private SortedMap testMap=new TreeMap(new ArrayComparator());
私有类ArrayComparator实现Comparator{
@凌驾
公共整数比较(整数[]o1,整数[]o2){
int结果=0;
int maxLength=数学最大值(o1.length,o2.length);
对于(int index=0;index
此实现可以针对其他数组进行调整,您必须注意的唯一一点是,equal Array(=具有相等成员的equal length)必须返回0,并且您具有确定顺序这里有一个使用TreeMap、Comparator接口和java方法java.util.arrays.equals(byte[],byte[])的解决方案
注:图中的顺序与此方法无关
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;
}
}
SortedMap testMap=newtreemap(newarraycomparator());
静态类ArrayComparator实现比较器{
@凌驾
公共整数比较(字节[]byteArray1,字节[]byteArray2){
int结果=0;
布尔值areEquals=Arrays.equals(byteArray1,byteArray2);
如果(!arequals){
结果=-1;
}
返回结果;
}
}
此外,我们可以像这样创建自己的自定义字节哈希映射
ByteHashMap byteMap = new ByteHashMap();
byteMap.put(keybyteArray,valueByteArray);
下面是完整的实现
public class ByteHashMap implements Map<byte[], byte[]>, Cloneable,
Serializable {
private Map<ByteArrayWrapper, byte[]> internalMap = new HashMap<ByteArrayWrapper, byte[]>();
public void clear() {
internalMap.clear();
}
public boolean containsKey(Object key) {
if (key instanceof byte[])
return internalMap.containsKey(new ByteArrayWrapper((byte[]) key));
return internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<byte[], byte[]>> entrySet() {
Iterator<java.util.Map.Entry<ByteArrayWrapper, byte[]>> iterator = internalMap
.entrySet().iterator();
HashSet<Entry<byte[], byte[]>> hashSet = new HashSet<java.util.Map.Entry<byte[], byte[]>>();
while (iterator.hasNext()) {
Entry<ByteArrayWrapper, byte[]> entry = iterator.next();
hashSet.add(new ByteEntry(entry.getKey().data, entry
.getValue()));
}
return hashSet;
}
public byte[] get(Object key) {
if (key instanceof byte[])
return internalMap.get(new ByteArrayWrapper((byte[]) key));
return internalMap.get(key);
}
public boolean isEmpty() {
return internalMap.isEmpty();
}
public Set<byte[]> keySet() {
Set<byte[]> keySet = new HashSet<byte[]>();
Iterator<ByteArrayWrapper> iterator = internalMap.keySet().iterator();
while (iterator.hasNext()) {
keySet.add(iterator.next().data);
}
return keySet;
}
public byte[] put(byte[] key, byte[] value) {
return internalMap.put(new ByteArrayWrapper(key), value);
}
@SuppressWarnings("unchecked")
public void putAll(Map<? extends byte[], ? extends byte[]> m) {
Iterator<?> iterator = m.entrySet().iterator();
while (iterator.hasNext()) {
Entry<? extends byte[], ? extends byte[]> next = (Entry<? extends byte[], ? extends byte[]>) iterator
.next();
internalMap.put(new ByteArrayWrapper(next.getKey()), next
.getValue());
}
}
public byte[] remove(Object key) {
if (key instanceof byte[])
return internalMap.remove(new ByteArrayWrapper((byte[]) key));
return internalMap.remove(key);
}
public int size() {
return internalMap.size();
}
public Collection<byte[]> values() {
return internalMap.values();
}
private final class ByteArrayWrapper {
private final byte[] data;
public ByteArrayWrapper(byte[] data) {
if (data == null) {
throw new NullPointerException();
}
this.data = data;
}
public boolean equals(Object other) {
if (!(other instanceof ByteArrayWrapper)) {
return false;
}
return Arrays.equals(data, ((ByteArrayWrapper) other).data);
}
public int hashCode() {
return Arrays.hashCode(data);
}
}
private final class ByteEntry implements Entry<byte[], byte[]> {
private byte[] value;
private byte[] key;
public ByteEntry(byte[] key, byte[] value) {
this.key = key;
this.value = value;
}
public byte[] getKey() {
return this.key;
}
public byte[] getValue() {
return this.value;
}
public byte[] setValue(byte[] value) {
this.value = value;
return value;
}
}
}
公共类ByteHashMap实现映射,可克隆,
可序列化{
私有映射internalMap=newhashmap();
公共空间清除(){
internalMap.clear();
}
公共布尔containsKey(对象键){
if(字节[]的键实例)
返回internalMap.containsKey(新的ByteArrayWrapper((字节[])键));
返回internalMap.containsKey(键);
}
公共布尔包含值(对象值){
返回internalMap.containsValue(值);
}
公共图书馆
public class ByteHashMap implements Map<byte[], byte[]>, Cloneable,
Serializable {
private Map<ByteArrayWrapper, byte[]> internalMap = new HashMap<ByteArrayWrapper, byte[]>();
public void clear() {
internalMap.clear();
}
public boolean containsKey(Object key) {
if (key instanceof byte[])
return internalMap.containsKey(new ByteArrayWrapper((byte[]) key));
return internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<byte[], byte[]>> entrySet() {
Iterator<java.util.Map.Entry<ByteArrayWrapper, byte[]>> iterator = internalMap
.entrySet().iterator();
HashSet<Entry<byte[], byte[]>> hashSet = new HashSet<java.util.Map.Entry<byte[], byte[]>>();
while (iterator.hasNext()) {
Entry<ByteArrayWrapper, byte[]> entry = iterator.next();
hashSet.add(new ByteEntry(entry.getKey().data, entry
.getValue()));
}
return hashSet;
}
public byte[] get(Object key) {
if (key instanceof byte[])
return internalMap.get(new ByteArrayWrapper((byte[]) key));
return internalMap.get(key);
}
public boolean isEmpty() {
return internalMap.isEmpty();
}
public Set<byte[]> keySet() {
Set<byte[]> keySet = new HashSet<byte[]>();
Iterator<ByteArrayWrapper> iterator = internalMap.keySet().iterator();
while (iterator.hasNext()) {
keySet.add(iterator.next().data);
}
return keySet;
}
public byte[] put(byte[] key, byte[] value) {
return internalMap.put(new ByteArrayWrapper(key), value);
}
@SuppressWarnings("unchecked")
public void putAll(Map<? extends byte[], ? extends byte[]> m) {
Iterator<?> iterator = m.entrySet().iterator();
while (iterator.hasNext()) {
Entry<? extends byte[], ? extends byte[]> next = (Entry<? extends byte[], ? extends byte[]>) iterator
.next();
internalMap.put(new ByteArrayWrapper(next.getKey()), next
.getValue());
}
}
public byte[] remove(Object key) {
if (key instanceof byte[])
return internalMap.remove(new ByteArrayWrapper((byte[]) key));
return internalMap.remove(key);
}
public int size() {
return internalMap.size();
}
public Collection<byte[]> values() {
return internalMap.values();
}
private final class ByteArrayWrapper {
private final byte[] data;
public ByteArrayWrapper(byte[] data) {
if (data == null) {
throw new NullPointerException();
}
this.data = data;
}
public boolean equals(Object other) {
if (!(other instanceof ByteArrayWrapper)) {
return false;
}
return Arrays.equals(data, ((ByteArrayWrapper) other).data);
}
public int hashCode() {
return Arrays.hashCode(data);
}
}
private final class ByteEntry implements Entry<byte[], byte[]> {
private byte[] value;
private byte[] key;
public ByteEntry(byte[] key, byte[] value) {
this.key = key;
this.value = value;
}
public byte[] getKey() {
return this.key;
}
public byte[] getValue() {
return this.value;
}
public byte[] setValue(byte[] value) {
this.value = value;
return value;
}
}
}