【问题标题】:What happens when a duplicate key is put into a HashMap?将重复键放入 HashMap 时会发生什么?
【发布时间】:2010-12-12 19:07:01
【问题描述】:

如果我多次将同一个键传递给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 是否像一个桶,其中最后一个值被引用(但在内存中)?

【问题讨论】:

标签: java hashmap hashtable


【解决方案1】:

根据定义,put 命令会替换与映射中给定键关联的先前值(在概念上类似于基本类型的数组索引操作)。

地图只是删除了对值的引用。如果没有其他东西持有对该对象的引用,那么该对象就有资格进行垃圾回收。此外,Java 返回与给定键关联的任何先前值(或 null,如果不存在),因此您可以确定其中的内容并在必要时维护引用。

更多信息在这里:HashMap Doc

【讨论】:

  • 谢谢。尽管阅读 Java 文档并没有清楚地提到这一点。我猜该文档的作者认为这是对所有哈希映射实现的默认假设。
  • 我正在阅读Java实现,似乎当您插入一个新的键值对时,它需要遍历桶中的所有元素才能知道键是否存在,所以它可以' t 只是在桶的末尾添加一个元素。这使得插入不是 100% O(1)
【解决方案2】:

您可以在 Map#put(K, V) 的 javadoc 中找到您的答案(它实际上返回了一些东西):

public V put(K key,
             V value)

将指定值与此映射中的指定键相关联 (可选操作)。如果地图 以前包含一个映射 这个键,旧值被替换为 指定的值。 (一张地图m据说 包含键 k 的映射 if 只有m.containsKey(k) 会 返回true。)

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

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

因此,如果您在调用mymap.put("1", "a string") 时不分配返回值,它就会变成未引用的,因此可以进行垃圾回收。

【讨论】:

  • returned value 是上面 javadoc 中记录的 previous value(或 null)所以,是的,这就是我意思是。真的会被误解吗?
  • 这很有帮助。
【解决方案3】:

它是键/值功能,您不能为多个值设置重复键,因为当您想要获取其中一个值属于输入键的实际值时
在您的示例中,当您想要获取值时“1”是哪一个?!
这是为每个值都拥有唯一键的原因,但你可以通过 java 标准库获得一个技巧:

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

【讨论】:

  • 很好的答案!好工作。你真的拯救了我的编程生活:)。
  • 也感谢我!我确实必须向它添加一个“删除”方法来执行与普通地图相同的功能,但效果很好!
  • @JGlass 您的欢迎老兄,但这不是技术解决方案,这是您可以通过 java 标准库执行的操作,在技术问题中您必须注意您的问题,如果您需要这种行为我确定这不是因为键/值概念的解决方案,并且必须考虑问题并找到解决问题的逻辑方法。无论如何,我的细节只是用 java 做的有趣方式,在生产中,问题和解决途径与有趣的工作有很大不同!但是当键/值行为不是您的问题并且发现具有这样的数据结构时,您可以使用它。
【解决方案4】:

替换地图中相应键的现有值。如果不存在具有相同名称的键,则它会使用提供的值创建一个键。 例如:

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

输出 键=“1”,值=“二”

所以,之前的值会被覆盖。

【讨论】:

    【解决方案5】:

    键的先前值被删除并替换为新值。

    如果您想保留给定键的所有值,您可以考虑实现以下内容:

    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));
             }
          }
       }
    }
    

    【讨论】:

    • 此解决方案已弃用。 MultiHashMap 是 apache.commons.collections 的一部分,而不是 java。
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

      关于地图是否像水桶的问题:否。

      这就像一个包含 name=value 对的列表,而 name 不需要是字符串(但它可以)。

      要获取一个元素,您需要将您的密钥传递给 get() 方法,该方法会为您提供分配的对象作为回报。

      哈希映射意味着如果您尝试使用 get 方法检索对象,它不会将真实对象与您提供的对象进行比较,因为它需要遍历它的列表并比较()你为当前元素提供的键。

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

      如果您想了解更多相关信息,您可能想查看javapractices.comtechnofundo.com 上的文章

      问候

      【讨论】:

        【解决方案8】:

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

        • 充其量新值将覆盖以前的值。

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

        没有重复:

        Stream.of("one").collect(Collectors.toMap(x -&gt; x, x -&gt; x))

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

        重复流:

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

        Exception java.lang.IllegalStateException: Duplicate key 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 标头可以具有相同的字段,...)

        祝你好运! :)

        【讨论】:

        • “覆盖”操作成本高吗?
        • 只能使用JDK来解决。 Collectors.toMap() 有第三个参数 - 合并函数。如果我们想简单地覆盖最后一个重复元素:Stream.of("one", "two", "one").collect(Collectors.toMap(x -&gt; x, x -&gt; x, (key1, key2) -&gt; key2))link
        • 您的第二个代码示例也不正确。此输入:"one", "not one", "surely not one" 不会产生任何重复键错误,因为所有字符串都不同。
        • 嗨@standalone。请仔细阅读映射功能(toMap)。
        • 嗨@WitoldKaczurba。请在发布之前编译您的代码。
        【解决方案9】:

        我一直用:

        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");
        }
        

        【讨论】:

          【解决方案10】:

          顺便说一句,如果您想要一些语义,例如仅在此键不存在时才放。您可以将concurrentHashMapputIfAbsent() 功能一起使用。 看看这个:

          https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html#put(K,%20V)

          concurrentHashMap 是线程安全的,具有高性能,因为它使用“lock striping”机制来提高吞吐量。

          【讨论】:

            【解决方案11】:

            是的,这意味着所有有值的 1 键都被最后添加的值覆盖,在这里你添加“肯定不是一个”,所以它只会显示“肯定不是一个”。

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

            【讨论】:

              【解决方案12】:
                       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<>();
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 2015-09-01
                • 1970-01-01
                • 2012-09-15
                • 2014-12-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-02-23
                相关资源
                最近更新 更多