将重复的密钥放入HashMap后会发生什么?


276

如果我多次将相同的键传递给HashMapput方法,那么原始值会怎样?如果值重复出现该怎么办?我没有找到任何文档。

情况1:密钥的覆盖值

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
System.out.println(mymap.get("1"));

我们得到surely not one

情况2:重复值

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
// The following line was added:
mymap.put("1","one");
System.out.println(mymap.get("1"));

我们得到one

但是其他值怎么办?我在向学生教授基础知识,有人问我这个问题。是Map等,其中最后一个值引用(但在内存中)水桶?


7
顺便说一句,这是一个很好的机会来展示属于Jakarta collections类(commons.apache.org/collections)的multi-hashmap 。在需要的时候,它将使您拥有与该键关联的任意数量的值。
约翰·芒什

Answers:


303

根据定义,该put命令将替换与映射中给定键相关联的先前值(概念上类似于对原始类型的数组索引操作)。

映射只是将其引用删除到该值。如果没有其他内容引用该对象,则该对象将有资格进行垃圾回收。另外,Java返回与给定键关联的任何先前值(null如果不存在,则返回任何值),因此您可以确定其中所包含的值,并在必要时维护引用。

此处更多信息:HashMap Doc


谢谢你 尽管阅读Java文档,但并没有明确提及。我猜文档的作者假定这是所有哈希映射实现的默认设置。
Andrew S

77

您可能会在Map#put(K,V)的javadoc中找到答案(实际上返回了一些内容):

public V put(K key,
             V value)

将指定值与该映射中的指定键关联(可选操作)。如果该映射先前包含此键的映射,则旧值将替换为指定值。(当且仅当将返回时,才m认为映射包含键的映射。)km.containsKey(k)true

参数:
key -与指定值关联的键。
value-与指定键关联的值。

返回:
与指定键关联的先前值,或者null如果没有映射key。(如果实现支持值,则null返回值还可以指示先前null与指定的关联的映射。)keynull

因此,如果在调用时不分配返回值mymap.put("1", "a string"),则该值将变为未引用状态,因此有资格进行垃圾回收。


3
返回的值前值(或null)作为记录略高于javadoc的话,没错,这就是我的意思。真的会被误解吗?
Pascal Thivent,09年

这非常有帮助。
roottraveller

18

密钥的先前值将被删除,并用新的替代。

如果您想保留所有值,则可以考虑实现如下所示:

import org.apache.commons.collections.MultiHashMap;
import java.util.Set;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
public class MultiMapExample {

   public static void main(String[] args) {
      MultiHashMap mp=new MultiHashMap();
      mp.put("a", 10);
      mp.put("a", 11);
      mp.put("a", 12);
      mp.put("b", 13);
      mp.put("c", 14);
      mp.put("e", 15);
      List list = null;

      Set set = mp.entrySet();
      Iterator i = set.iterator();
      while(i.hasNext()) {
         Map.Entry me = (Map.Entry)i.next();
         list=(List)mp.get(me.getKey());

         for(int j=0;j<list.size();j++)
         {
          System.out.println(me.getKey()+": value :"+list.get(j));
         }
      }
   }
}

1
此解决方案已描述。MultiHashMap是apache.commons.collections的一部分,而不是Java。
wikimix 2015年

17

它是“键/值”功能,您不能为多个值使用重复键,因为当您想获取
“ 1”的值时,当您想获取其中一个值属于输入键的实际值时,吗?!
这就是每个值都具有唯一键的原因,但是您可以通过java标准lib来获得技巧:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class DuplicateMap<K, V> {

    private Map<K, ArrayList<V>> m = new HashMap<>();

    public void put(K k, V v) {
        if (m.containsKey(k)) {
            m.get(k).add(v);
        } else {
            ArrayList<V> arr = new ArrayList<>();
            arr.add(v);
            m.put(k, arr);
        }
    }

     public ArrayList<V> get(K k) {
        return m.get(k);
    }

    public V get(K k, int index) {
        return m.get(k).size()-1 < index ? null : m.get(k).get(index);
    }
}


您可以通过以下方式使用它:

    public static void main(String[] args) {
    DuplicateMap<String,String> dm=new DuplicateMap<>();
    dm.put("1", "one");
    dm.put("1", "not one");
    dm.put("1", "surely not one");
    System.out.println(dm.get("1"));
    System.out.println(dm.get("1",1));
    System.out.println(dm.get("1", 5));
}

打印结果为:

[one, not one, surely not one]
not one
null

好答案!做得好。您从字面上拯救了我的编程生命:)。
Subin Babu

也感谢我!我确实必须向其添加“删除”方法以执行与普通地图相同的功能,但是效果很好!
JGlass

1
@JGlass欢迎您,但是这不是技术解决方案,这是Java标准lib可以做的,从技术上讲,您必须注意自己的问题,如果需要这种行为,我敢肯定这不是解决方案,因为是键/值概念的基础,必须考虑问题并找到解决的逻辑方法。无论如何,我的细节只是与Java和生产相关的有趣方式,问题和解决途径与有趣的工作截然不同!但是当键/值行为不是您的问题并且发现具有这样的数据结构时,您可以使用它。
java acm

13

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


12

它将映射中的现有值替换为各个键。如果不存在具有相同名称的键,则它将使用提供的值创建一个键。例如:

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","two");

输出 键=“ 1”,值=“两个”

因此,先前的值将被覆盖。


4

向您询问地图是否像水桶一样:不。

这就像一个name=value成对的列表,而name不必是一个String(尽管可以)。

要获取元素,请将密钥传递给get()方法,该方法为您提供分配的对象。

而一个哈希地图意味着,如果你想使用Get-方法来检索您的对象,也不会比较真实的对象提供给您的一个,因为它会通过其名单需要遍历和比较()的关键您提供了当前元素。

这将是低效率的。相反,无论您的对象由什么组成,它都会从两个对象中计算出一个所谓的哈希码并将其进行比较。比较两个ints而不是两个整个对象(可能非常复杂)比较容易。您可以想象哈希码就像一个具有预定义长度(int)的摘要,因此它不是唯一的并且有冲突。您可以在我向其插入链接的文档中找到哈希码的规则。

如果您想了解更多关于这一点,你可能想看看文章对javapractices.comtechnofundo.com

问候


3

我一直使用:

HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();

如果我想将多个事物应用于一个识别码。

public void MultiHash(){
    HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();
    String key = "Your key";

    ArrayList<String> yourarraylist = hashy.get(key);

    for(String valuessaved2key : yourarraylist){
        System.out.println(valuessaved2key);
    }

}

您总是可以做这样的事情,让自己成为一个迷宫!

public void LOOK_AT_ALL_THESE_HASHMAPS(){
    HashMap<String, HashMap<String, HashMap<String, HashMap<String, String>>>> theultimatehashmap = new HashMap <String, HashMap<String, HashMap<String, HashMap<String, String>>>>();
    String ballsdeep_into_the_hashmap = theultimatehashmap.get("firststring").get("secondstring").get("thirdstring").get("forthstring");
}

2

来自JDK的映射不用于在重复键下存储数据。

  • 最好情况下,新值将覆盖以前的值。

  • 更糟糕的情况是例外(例如,当您尝试将其作为流收集时):

无重复:

Stream.of("one").collect(Collectors.toMap(x -> x, x -> x))

好。您将获得:$ 2 ==> {one = one}

流重复:

Stream.of("one", "not one", "surely not one").collect(Collectors.toMap(x -> 1, x -> x))

异常java.lang.IllegalStateException:重复键1(尝试合并值1而不是1)| 在Collectors.duplicateKeyException(Collectors.java:133)中| 在Collectors.lambda $ uniqKeysMapAccumulator $ 1(Collectors.java:180)中| 在ReduceOps $ 3ReducingSink.accept(ReduceOps.java:169)| 在Spliterators $ ArraySpliterator.forEachRemaining(Spliterators.java:948)| 在AbstractPipeline.copyInto(AbstractPipeline.java:484)| 在AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)| 在ReduceOps $ ReduceOp.evaluateSequential(ReduceOps.java:913)中| 在AbstractPipeline.evaluate(AbstractPipeline.java:234)| 在ReferencePipeline.collect(ReferencePipeline.java:578)中| 在(#4:1)

要处理重复的键-使用其他程序包,例如:https : //google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html

还有许多其他处理重复键的实现。Web需要这些(例如重复的cookie键,Http标头可以具有相同的字段,...)

祝好运!:)


“超控”操作成本高吗?
gaurav

只能使用JDK来解决。Collectors.toMap()有第三个参数-合并功能。如果我们只想覆盖最后一个重复的元素:Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2))链接
独立运行

您的第二个代码示例也不正确。此输入:"one", "not one", "surely not one"由于所有字符串都不相同,因此不会产生任何重复的键错误。
独立

嗨,@standalone。请仔细阅读映射功能(toMap)。
Witold Kaczurba,

嗨@WitoldKaczurba。请在发布之前编译您的代码。
独立运行


1

是的,这意味着所有带有值的1个键都将被最后一个添加的值覆盖,并且在此处添加“肯定不是一个”,因此它只会显示“肯定不是一个”。

即使试图显示循环,它也将只显示一个键和具有相同键的值。


0
         HashMap<Emp, Emp> empHashMap = new HashMap<Emp, Emp>();

         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp());
         empHashMap.put(new Emp(1), new Emp());
         System.out.println(empHashMap.size());
    }
}

class Emp{
    public Emp(){   
    }
    public Emp(int id){
        this.id = id;
    }
    public int id;
    @Override
    public boolean equals(Object obj) {
        return this.id == ((Emp)obj).id;
    }

    @Override
    public int hashCode() {
        return id;
    }
}


OUTPUT : is 1

意味着,如果您正确覆盖了equals和hashCode()方法,则哈希映射将不允许重复。

HashSet还内部使用HashMap,请参见源文档

public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.