HashMap包含几个具有相同值的不同键?

时间:2016-03-01 11:03:17

标签: java arraylist hashmap immutability mutable

我做的很简单:我想创建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<String, Integer> aPair的声明放入for循环中。新结果是我想要的结果:

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

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

编辑:正如@Eran在下面的评论中所提到的,自定义的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());
}

4 个答案:

答案 0 :(得分:1)

你在危险的水域踩踏,因为你的钥匙是可变的,请阅读这个为什么它不是一个好主意 - Are mutable hashmap keys a dangerous practice?

嗯,你的例子就说明了为什么它不是一个好主意。在地图中添加1个键实例,然后对其进行修改,从而有效地修改hashmap中的所有键值对。

答案 1 :(得分:1)

您的第一个代码段不起作用,因为您正在改变已作为Pair中的密钥的同一HashMap实例。这允许相同的Pair实例在HashMap的多个条目中显示为键(因为在您修改hashCode实例后计算的新Pair映射到新的HashMap HashMap不包含任何条目的存储桶,并有效地破坏HashMap

您不应该改变作为HashMap密钥的实例(除非您在更新之前将其从Map中删除,并将更新后的版本放入{{1}}更高版本)。

答案 2 :(得分:1)

  

如果对象的hashCode()值可以根据其状态改变,那么我们   在基于哈希的键中使用此类对象时必须小心   集合,以确保我们不允许他们的状态改变时   它们被用作哈希键。所有基于散列的集合都假定   对象的哈希值在使用时不会改变   集合中的关键。如果密钥的哈希码在其中发生变化   是一个集合,一些不可预测和混乱的后果   可以跟着。这在实践中通常不是问题 - 事实并非如此   通常的做法是使用像List这样的可变对象作为关键字   HashMap中。

从此answer

答案 3 :(得分:0)

在第一个代码段中,您实际上是为每个地图条目使用相同的密钥对象。您只需修改它的左值,但它仍然指向相同的内存地址。映射需要具有唯一键(每个键必须指向不同的内存地址),这就是为什么需要为每个映射条目添加新的对实例。