如何创建具有两个键(键对、值)的 HashMap?

新手上路,请多包涵

我有一个二维整数数组。我希望将它们放入 HashMap 中。但我想根据数组索引访问 HashMap 中的元素。就像是:

对于 A[2][5], map.get(2,5) 返回与该键关联的值。但是如何使用一对键创建一个 hashMap 呢?或者一般来说,多个键: Map<((key1, key2,..,keyN), Value) 我可以使用 get(key1,key2,…keyN) 访问元素。

编辑:发布问题 3 年后,我想添加更多内容

我遇到了另一种方式 NxN matrix

数组索引 ij 可以表示为单个 key 如下方式:

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

并且可以通过以下方式从 key 中检索索引:

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

原文由 Crocode 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

有几种选择:

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 来自番石榴

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

table.get(2, 5);

Table 使用下面 _的地图_。

N 维

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

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

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

也许看看你有元组和 case 类的 Scala(用单线替换整个 Key 类)。

原文由 Tomasz Nurkiewicz 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

首先,您应该了解实施 hashCode()equals() 。您将需要这样做。

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

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

实际上是你能做的最糟糕的实现之一。原因很简单:你有很多相等的哈希值!而 hashCode() 应该返回 int 值,这些值往往很少见,最好是独一无二的。使用这样的东西:

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

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

第三,在实施 equals() 时,还要知道它的用途并了解如何创建密钥,因为它们是对象。通常你会做不必要的 if 语句,因为你总是会得到相同的结果。

如果您像这样创建密钥: 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;
  }

}

您可以为维度索引 XY 提供公共访问级别,因为它们是最终的并且不包含敏感信息。我不是 100% 确定 private 在将 Object 转换为 Key -8-cea992 时,访问级别是否在 任何 情况下都能正常工作

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

原文由 chrixle 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题