存储现有值时,Java的HashMap密钥替换

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了存储现有值时,Java的HashMap密钥替换相关的知识,希望对你有一定的参考价值。

根据Java HashMap文档,put方法替换了以前包含的值(如果有的话):https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#put-K-V-

将指定的值与此映射中的指定键相关联。如果映射先前包含键的映射,则替换旧值。

然而,文档没有说明存储新值时(现有)密钥会发生什么。现有密钥是否被替换?或结果未定义?

请考虑以下示例:

public class HashMapTest
{

   private static class Key {
      private String value;
      private Boolean b;

      private Key(String value, Boolean b) {
         this.value = value;
         this.b = b;
      }

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

      @Override
      public boolean equals(Object obj)
      {
         if (obj instanceof Key)
         {
            return value.equals(((Key)obj).value);
         }
         return false;
      }

      @Override
      public String toString()
      {
         return "(" + value.toString() + "-" + b + ")";
      }
   }

   public static void main(String[] arg) {

      Key key1 = new Key("foo", true);
      Key key2 = new Key("foo", false);

      HashMap<Key, Object> map = new HashMap<Key, Object>();
      map.put(key1, 1L);

      System.out.println("Print content of original map:");
      for (Entry<Key, Object> entry : map.entrySet()) {
         System.out.println("> " + entry.getKey() + " -> " + entry.getValue());
      }

      map.put(key2, 2L);

      System.out.println();
      System.out.println("Print content of updated map:");
      for (Entry<Key, Object> entry : map.entrySet()) {
         System.out.println("> " + entry.getKey() + " -> " + entry.getValue());
      }
   }
}

当我使用Oracle jdk1.8.0_121执行以下代码时,会生成以下输出:

Print content of original map:
> (foo-true) -> 1

Print content of updated map:
> (foo-true) -> 2

证据表明(至少在我的电脑上)现有密钥不会被替换。

这是预期/定义的行为(它在何处定义?)或者它只是所有可能结果中的一个?我可以指望这种行为在所有Java平台/版本中保持一致吗?

编辑:这个问题不是What happens when a duplicate key is put into a HashMap?的重复。我问的是密钥(即当你使用多个引用相同逻辑密钥的密钥实例时),而不是关于值。

答案

从查看source,它没有被取代,我不确定它是否由合同保证。

if (e != null) { // existing mapping for key
    V oldValue = e.value;
    if (!onlyIfAbsent || oldValue == null)
        e.value = value;
    afterNodeAccess(e);
    return oldValue;
}

它找到了现有的映射并替换了值,没有用新密钥完成,它们应该是相同且不可变的,所以即使不同的实现可以替换密钥也无关紧要。

你不能指望这种行为,但你应该以一种无关紧要的方式编写你的代码。

另一答案

添加新对时,地图使用hasCodeequals来检查地图中是否已存在该关键字。如果密钥已存在,则旧值将替换为新值。钥匙本身仍未修改。

Map<Integer,String> map = new HashMap<>();
map.put(1,"two"); 
System.out.println(map); // {1=two}
map.put(1,"one"); 
System.out.println(map); // {1=one}
map.put(2,"two");
System.out.println(map); // {1=one, 2=two}

您的equalshashCode合同存在问题。根据您的实施情况,ke1key2完全相同:

@Override
public boolean equals(Object obj)
{
    if (obj instanceof Key)
    {
       return value.equals(((Key)obj).value);
    }
    return false;
}

你还需要比较Boolean b

Key other = (Key) obj;
return value.equals(other.value) && b.equals(other.b);

同样的规则适用于hasCode

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

return value.hashCode() + b.hashCode();

随着这些变化key1key2是不同的

System.out.println(key1.equals(key2));

并且地图的输出将是

> (foo-true) -> 1
> (foo-false) -> 2
另一答案

它没有被替换 - 它也不应该被替换。如果你知道HashMap如何工作以及hashCodeequals是什么(或更确切地说是如何使用它们) - 不接触Key的决定是显而易见的。

当你把另一个Key / Entry第二次放在地图中时,那个键首先在地图中查找 - 根据hashCode/equals,所以根据map,IFF键具有相同的hashCode并且根据equals相等它们是相同。如果是这样,为什么要更换它?特别是因为如果它已经被替换,那么可能需要操作额外的操作或至少额外的代码,以便在键相等时不触发任何其他操作。

以上是关于存储现有值时,Java的HashMap密钥替换的主要内容,如果未能解决你的问题,请参考以下文章

当我们知道HashMap中的值时获取密钥[重复]

java-映射到存储密钥和多种类型的键

java 测试的HashMap和ConcurrentHashMap中是否可以存储密钥为空的数据,测试显示前者可以,后者是不可以的,会在看跌的时候校验密钥是否为空,如果为空则会抛出数Npe

java- hashmap一个键和多个值替代将值部分存储在List中

KVC:如何测试现有密钥

Java HashMap原理