根据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?不重复。我问的是密钥(即当你使用多个引用相同逻辑密钥的密钥实例时),而不是关于值。
答案 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)
添加新对后,地图会使用hasCode
,equals
来检查地图中是否已存在该密钥。如果密钥已存在,则旧值将替换为新值。钥匙本身仍未修改。
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}
您的equals
和hashCode
合同存在问题。 ke1
和key2
根据您的实施情况相同:
@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();
这些更改key1
和key2
不同
System.out.println(key1.equals(key2));
并且地图的输出将是
> (foo-true) -> 1
> (foo-false) -> 2
答案 2 :(得分:0)
它没有被替换 - 它也不应该被替换。如果您知道 HashMap
如何运作以及hashCode
和equals
是什么(或者更确切地说是如何使用它们) - 决定不接触{{} 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实现的不同而异,但它可能会提供更强大的功能 确保将来可能不会改变