【问题标题】:HashMap contains several different keys having the same value? [duplicate]HashMap 包含几个具有相同值的不同键? [复制]
【发布时间】:2016-03-01 11:03:16
【问题描述】:

我所做的很简单:我想创建一个HashMap<Pair, ArrayList<Integer>>,其中Pair 作为键,ArrayList<Integer> 作为值。 Pair 是自定义类,包含元素l(左)和r(右)。

一开始,我是这样做的:

Map<Pair, ArrayList<Integer>> hashmap = new HashMap<>();
ArrayList<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
stringList.add("a");
Pair<String, Integer> aPair = new Pair<>(" ", 1); // HERE will be changed!

for (String aString: stringList) {
  aPair.setLeft(aString);
  if (!hashmap.containsKey(aPair)){
    hashmap.put(aPair, new ArrayList<Integer>());
  }
  hashmap.get(aPair).add(1);
}

for (Map.Entry<Pair, ArrayList<Integer>> entry: hashmap.entrySet()) {
  out.println(entry.getKey().getLeft() + " " + entry.getKey().getRight() + " " + entry.getValue());
}

但是输出是:

a 1 [1]
a 1 [1]
a 1 [1, 1]

但是,如果我把上面的代码改成下面这样:

Map<Pair, ArrayList<Integer>> hashmap = new HashMap<>();
ArrayList<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
stringList.add("a");

for (String aString: stringList) {
  Pair<String, Integer> aPair = new Pair<>(aString, 1); // HERE changed!
  if (!hashmap.containsKey(aPair)){
    hashmap.put(aPair, new ArrayList<Integer>());
  }
  hashmap.get(aPair).add(1);
}

for (Map.Entry<Pair, ArrayList<Integer>> entry: hashmap.entrySet()) {
  out.println(entry.getKey().getLeft() + " " + entry.getKey().getRight() + " " + entry.getValue());
}

所做的更改是将 Pair&lt;String, Integer&gt; aPair 的声明放入 for 循环中。新的结果是我想要的如下:

c 1 [1]
b 1 [1]
a 1 [1, 1]

为什么会这样? Here 是一个类似的问题。但它仍然不同。

编辑:正如@Eran 在下面的cmets 中提到的,自定义的Pair 覆盖了hashCode()equals() 方法

@Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }

@Override
public boolean equals(Object o) {
  if (!(o instanceof Pair)) return false;
  Pair<?, ?> pairo = (Pair<?, ?>) o;
  return this.left.equals(pairo.getLeft()) &&
         this.right.equals(pairo.getRight());
}

【问题讨论】:

  • 是的。它们被覆盖。 @Eran

标签: java arraylist hashmap immutability mutable


【解决方案1】:

如果一个对象的 hashCode() 值可以根据它的状态而改变,那么我们 使用这些对象作为基于哈希的键时必须小心 集合以确保我们不允许它们的状态在何时更改 它们被用作哈希键。所有基于哈希的集合都假设 一个对象的哈希值在它被用作一个对象时不会改变 集合中的关键。如果一个键的哈希码在它发生变化时 在一个集合中,一些不可预测和令人困惑的后果 可以跟随。这在实践中通常不是问题——它不是 使用可变对象(如 List)作为 a 中的键的常见做法 哈希映射。

来自answer

【讨论】:

    【解决方案2】:

    由于您的密钥是可变的,因此您正踏入危险的水域,请阅读这篇文章,为什么这不是一个好主意 - Are mutable hashmap keys a dangerous practice?

    好吧,仅您的示例就说明了为什么这不是一个好主意。您在 map 中添加 1 个 key 实例,然后对其进行修改,从而有效地修改了 hashmap 中的所有键值对。

    【讨论】:

      【解决方案3】:

      您的第一个 sn-p 不起作用,因为您正在变异相同的 Pair 实例,该实例已经用作 HashMap 中的键。这允许相同的Pair 实例作为键出现在HashMap 的多个条目中(因为在修改Pair 实例后计算的新hashCode 映射到HashMap 的新存储桶,它不会'不包含任何条目)并有效地破坏HashMap

      您不应该对用作HashMap 键的实例进行变异(除非您在更新之前将其从HashMap 中删除,然后将更新后的版本放入Map)。

      【讨论】:

      • 是否有map 的knid,其中key 可以更改?
      • @ChangLiu 可以更改key,但是你必须在更改之前将它删除并在更改之后重新添加它,以便Map将它存储在以后可以找到的地方。
      • 是的,我知道这一点。我想知道的是:您无法就地更新key
      • @ChangLiu 我不知道。
      【解决方案4】:

      在第一个代码 sn-p 中,您实际上对 map 的每个条目使用相同的键对象。您只是修改它的左值,但它仍然指向相同的内存地址。 Map 需要有唯一的键(每个键必须指向不同的内存地址),这就是为什么您需要为每个映射条目放置新的 pair 实例。

      【讨论】:

        猜你喜欢
        • 2013-10-16
        • 2014-08-18
        • 1970-01-01
        • 2016-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-08
        相关资源
        最近更新 更多