存储现有值

时间:2017-09-28 14:04:59

标签: 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?不重复。我问的是密钥(即当你使用多个引用相同逻辑密钥的密钥实例时),而不是关于值。

4 个答案:

答案 0 :(得分:0)

source看,它没有被替换,我不确定它是否由合同保证。

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

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

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

答案 1 :(得分:0)

添加新对后,地图会使用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

答案 2 :(得分:0)

它没有被替换 - 它也不应该被替换。如果您知道 HashMap如何运作以及hashCodeequals是什么(或者更确切地说是如何使用它们) - 决定不接触{{} 1}}很明显。

当你第二次将其他键/条目放在地图中时,该键首先在地图中查找 - 根据Key,所以根据{ {1}} IFF密钥具有相同的hashCode,并且根据hashCode/equals它们是相同的。如果是这样,为什么要更换它?特别是因为如果它被替换了,那么可能会修复额外的操作,或者至少额外的代码如果键相等则不会触发其他任何东西。

答案 3 :(得分:0)

显然,当前的HashSet implementation依赖于此HashMap行为才能符合HashSet文档。 我的意思是,当您在HashSet中添加新元素时,文档说明如果您尝试在已经包含该元素的HasSet中添加元素,则HashSet不会更改,因此该元素不会被替换, 在openjdk8实现中,HashSet使用HashMap键保存值,在HashSet.add方法中,它调用HashMap.put方法添加值,因此依赖于put方法不会替代对象的事实

尽管这仍然不是文档中的直接规范,并且可能会因JRE实现的不同而异,但它可能会提供更强大的功能 确保将来可能不会改变