Java 使用 byte[] 作为 Map 中的键

Java use byte[] as key in a Map

我在使用 Java 地图时遇到了一些困难。 我想要一个以 byte[] 作为键和 returns 自定义对象作为值的映射。

首先我尝试使用哈希映射,但因为我习惯于使用不同的数组,但具有相同的值,所以它不起作用。同样使用 Arrays.hashCode() 是不可能的,因为具有不同值的多重数组将具有相同的哈希值。

现在我正在尝试使用 TreeMap 而不是 HashMap,但现在我不知道如何解决

Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(TreeMap.java:1294)
    at java.util.TreeMap.put(TreeMap.java:538)

当我尝试添加对象时抛出异常。

有人知道如何解决这个异常吗? 或者更具体地说,我应该如何以及向 TreeMap 提供哪个比较器?

好的,这里介绍 material 到 Java。

有两件事在起作用:

  1. 数组是基于身份的,而不是基于值的(有关更多详细信息,请参阅 this answer;不过,我找不到任何正式的规范)。

因此,以下returns false

byte[] a = {1};
byte[] b = {1};
System.out.println(a.equals(b));
  1. 数组未实现 Comparable (it would make no sense for them). In order to put anything into a TreeMap, you must either provide a Comparator for it (using this constructor), or it must implement Comparable

编辑:如果你确实有一些二进制数据想用作键(这是一个相当奇怪的情况),你可以:

  1. 使用 Java NIO 的 ByteBuffer and its ByteBuffer.wrap(byte[]) 方法(缺点:此 class 是可变的,但在您的原始版本中 byte[] 也是可变的)。

  2. byte[] 上创建自定义(最好是不可变的)包装器,并将此 class 用作 HashMap/TreeMap 的键(但是,如果您不需要排序,请不要出于性能原因使用TreeMap

示例自定义包装器的存根:

final class ByteWrapper {

    private final byte[] bytes;

    public ByteWrapper(byte[] bytes) {
        this.bytes = bytes;
    }

    @Override
    public boolean equals(Object o) {
        // type checks here
        ByteWrapper that = (ByteWrapper) o;
        return Arrays.equals(bytes, that.bytes);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(bytes);
    }
}

原因是您的密钥没有实现Comparable

一个解决方案是创建一个 class(包装器)用作密钥:

class ByteArrayKey implements Comparable{
    byte[] value;

    ...
}

您所要做的就是根据需要实施 compareTo(T o )(检查 Arrays),当然还要正确使用此实例。

这背后的优点是您不必在每个 TreeMap 中设置一个 Comparator,您将使用构造函数实例化:

public TreeMap(Comparator<? super K> comparator)

提供更加简洁和可维护的代码(如果等式发生变化,您只需更改一次)。