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

有帮助吗?

解决方案

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

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

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

  1. 包裹在一个 String, ,但是你必须小心编码问题(你需要确保 byte -> String -> byte 给你相同的字节)。
  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

(实际数字无关紧要;它们不同这一事实很重要。)

假设你 实际上 想要平等,我建议你创建自己的包装器,其中包含 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);
使用TreeMap中,比较器接口和java方法java.util.Arrays.equals的溶液

下面(字节[],字节[]);

请注意:在地图上的顺序是不相关的用该方法

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;
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top