Java 使用字节数组作为 Map 键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1058149/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:54:52  来源:igfitidea点击:

Using a byte array as Map key

javahashmapbytearray

提问by shikhar

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

您认为使用字节数组作为 Map 键有什么问题吗?我也可以做new String(byte[])和散列,String但使用更简单byte[]

采纳答案by Kathy Van Stone

The problem is that byte[]uses object identity for equalsand hashCode, so that

问题是byte[]equalsand使用对象标识hashCode,因此

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

will not match in a HashMap. I see three options:

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

  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 hashCodeand equalsto use the contents of the byte array.
  1. 包装在 a 中String,但是您必须注意编码问题(您需要确保字节 -> 字符串 -> 字节为您提供相同的字节)。
  2. 使用List<Byte>(在内存中可能很昂贵)。
  3. 做你自己的包装类,编写hashCodeequals使用字节数组的内容。

回答by Adam Paynter

I believe that arrays in Java do not necessarily implement the hashCode()and equals(Object)methods intuitively. That is, two identical byte arrays will not necessarily share the same hash code and they will not necessarily claim to be equal. Without these two traits, your HashMap will behave unexpectedly.

我认为 Java 中的数组不一定直观地实现hashCode()equals(Object)方法。也就是说,两个相同的字节数组不一定共享相同的哈希码,也不一定声称它们相等。如果没有这两个特征,您的 HashMap 将出现意外行为。

Therefore, I recommend againstusing byte[]as keys in a HashMap.

因此,我建议使用byte[]作为一个HashMap键。

回答by Jon Skeet

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 actuallywant equality, I suggest you create your own wrapper which contains a byte[]and implements equality and hash code generation appropriately:

假设您确实想要相等,我建议您创建自己的包装器,其中包含 abyte[]并适当地实现相等和哈希代码生成:

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 ByteArrayWrapperconstructor if you want, but obviously that will be a waste of performance if you know you won'tbe changing the contents of the byte array.

请注意,如果您在使用ByteArrayWrapper, 作为 a HashMap(等)中的键后更改字节数组中的值,则再次查找键时会遇到问题……如果需要,您可以在ByteArrayWrapper构造函数中复制数据,但显然,如果您知道不会更改字节数组的内容,那将浪费性能。

EDIT: As mentioned in the comments, you could also use ByteBufferfor 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.

编辑:如评论中所述,您也可以ByteBuffer为此使用(特别是它的ByteBuffer#wrap(byte[])方法)。考虑到ByteBuffer您不需要的所有额外能力,我不知道这是否真的正确,但这是一种选择。

回答by dfa

I see problems since you should use Arrays.equals and Array.hashCode, in place of default array implementations

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

回答by df.

Arrays.toString(bytes)

Arrays.toString(字节)

回答by Artem Oboturov

You could use java.math.BigInteger. It has a BigInteger(byte[] val)constructor. It's a reference type, so could be used as a key for hashtable. And .equals()and .hashCode()are defined as for respective integer numbers, which means BigInteger has consistent equals semantics as byte[] array.

你可以使用java.math.BigInteger. 它有一个BigInteger(byte[] val)构造函数。它是一种引用类型,因此可以用作哈希表的键。和.equals().hashCode()被定义为用于各个整数,这意味着具有BigInteger的一致平等语义byte []数组。

回答by byte_array

We can use ByteBuffer for this (This is basically the byte[] wrapper with a comparator)

我们可以为此使用 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)));

will print

将打印

true

回答by Christof R

You could also convert the byte[] to a 'safe' string using Base32 or Base64, for example:

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

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

of course there are many variants of the above, like:

当然,上面有许多变体,例如:

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

回答by Milind Patil

You should use create a class somthing like ByteArrKey and overload hashcode and equal methods, remember the contract between them.

你应该使用创建一个像 ByteArrKey 这样的类并重载 hashcode 和 equal 方法,记住它们之间的契约。

This will give you greater flexibility as you can skip 0 entries that are appended at the end of byte array, specially if you copy only some part form the other byte buffer.

这将为您提供更大的灵活性,因为您可以跳过附加在字节数组末尾的 0 个条目,特别是如果您仅从另一个字节缓冲区复制某些部分。

This way you will decide how both objects SHOULD be equal.

通过这种方式,您将决定两个对象应该如何相等。

回答by Thorsten S.

I am very surprised that the answers are not pointing out the most simple alternative.

我很惊讶答案没有指出最简单的选择。

Yes, it is not possible to use a HashMap, but nobody prevents you from using a SortedMap as alternative. The only thing is to write a Comparator which needs to compare the arrays. It is not as performant as a HashMap, but if you want a simple alternative, here you go (you can replace SortedMap with Map if you want to hide the implementation):

是的,不可能使用 HashMap,但没有人阻止您使用 SortedMap 作为替代方案。唯一的事情是编写一个需要比较数组的比较器。它的性能不如 HashMap,但是如果您想要一个简单的替代方案,那么您可以使用(如果您想隐藏实现,可以将 SortedMap 替换为 Map):

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

This implementation can be adjusted for other arrays, the only thing you must be aware of is that equal arrays (= equal length with equal members) must return 0 and that you have a determistic order

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