如何创建具有两个键(键对,值)的哈希映射?2 个维度N 个维度

2022-08-31 08:16:27

我有一个2D整数数组。我希望将它们放入HashMap中。但是我想从基于数组索引的HashMap访问元素。像这样:

对于 A[2][5],返回与该键关联的值。但是,如何创建具有一对键的哈希映射?或者一般来说,多个键:以一种我可以使用get(key1,key2,...键 N)。map.get(2,5)Map<((key1, key2,..,keyN), Value)

编辑:发布问题3年后,我想再添加一点

我遇到了另一种方式。NxN matrix

数组索引,并且可以通过以下方式表示为单个索引:ijkey

int key = i * N + j;
//map.put(key, a[i][j]); // queue.add(key); 

指数可以通过这种方式从 中收回:key

int i = key / N;
int j = key % N;

答案 1

有以下几个选项:

2 个维度

地图地图

Map<Integer, Map<Integer, V>> map = //...
//...

map.get(2).get(5);

包装键对象

public class Key {

    private final int x;
    private final int y;

    public Key(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Key)) return false;
        Key key = (Key) o;
        return x == key.x && y == key.y;
    }

    @Override
    public int hashCode() {
        int result = x;
        result = 31 * result + y;
        return result;
    }

}

实施和在这里至关重要。然后,您只需使用:equals()hashCode()

Map<Key, V> map = //...

和:

map.get(new Key(2, 5));

来自番石榴的桌子

Table<Integer, Integer, V> table = HashBasedTable.create();
//...

table.get(2, 5);

Table使用下面的地图地图。

N 个维度

请注意,特殊类是唯一缩放到 n 维的方法。您还可以考虑:Key

Map<List<Integer>, V> map = //...

但从性能的角度来看,以及可读性和正确性(没有简单的方法来强制执行列表大小),这很糟糕。

也许看看Scala,你有元组和类(用单行替换整个类)。caseKey


答案 2

当您创建自己的密钥对对象时,您应该面对一些事情。

首先,您应该了解实现 和 。您将需要执行此操作。hashCode()equals()

其次,在实现时,请确保您了解它的工作原理。给定的用户示例hashCode()

public int hashCode() {
    return this.x ^ this.y;
}

实际上是您可以做的最糟糕的实现之一。原因很简单:你有很多相等的哈希值!并且应该返回的int值往往是罕见的,在最好的情况下是唯一的。使用类似如下的内容:hashCode()

public int hashCode() {
  return (X << 16) + Y;
}

这很快,并且返回 -2^16 和 2^16-1(-65536 到 65535)之间的键的唯一哈希。这几乎适用于任何情况。你很少超出这个界限。

第三,在实现时还要知道它的用途,并注意如何创建键,因为它们是对象。通常,如果语句导致您始终具有相同的结果,则您执行不必要的操作。equals()

如果像这样创建密钥:您将永远不会比较密钥的引用。因为每次你想访问地图时,你都会做类似的事情。因此,您不需要像 .它永远不会发生。map.put(new Key(x,y),V);map.get(new Key(x,y));equals()if (this == obj)

而不是在你更好的使用.即使对于子类,它也有效。if (getClass() != obj.getClass())equals()if (!(obj instanceof this))

所以你唯一需要比较的实际上是X和Y。因此,在这种情况下,最好的实现是:equals()

public boolean equals (final Object O) {
  if (!(O instanceof Key)) return false;
  if (((Key) O).X != X) return false;
  if (((Key) O).Y != Y) return false;
  return true;
}

所以最后你的键类是这样的:

public class Key {

  public final int X;
  public final int Y;

  public Key(final int X, final int Y) {
    this.X = X;
    this.Y = Y;
  }

  public boolean equals (final Object O) {
    if (!(O instanceof Key)) return false;
    if (((Key) O).X != X) return false;
    if (((Key) O).Y != Y) return false;
    return true;
  }

  public int hashCode() {
    return (X << 16) + Y;
  }
    
}

您可以为维度索引和公共访问级别指定,因为它们是最终的,不包含敏感信息。我不是100%确定访问级别在任何情况下是否正常工作,当转换为.XYprivateObjectKey

如果你想知道决赛,我会将任何东西声明为最终值,哪个值是在实例化时设置的,并且永远不会改变 - 因此是一个对象常量。