关于java:将重复键放入HashMap时会发生什么?

What happens when a duplicate key is put into a HashMap?

如果我多次将相同的密钥传递给HashMapput方法,原始值会发生什么变化? 如果价值重复怎么办? 我没有找到任何关于此的文件。

案例1:密钥的覆盖值

1
2
3
4
5
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:重复值

1
2
3
4
5
6
7
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是否像引用最后一个值的存储桶(但在内存中)?


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

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

更多信息:HashMap Doc


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

1
2
public V put(K key,
             V value)

Associates the specified value with the specified key in this map
(optional operation). If the map
previously contained a mapping for
this key, the old value is replaced by
the specified value. (A map m is said
to contain a mapping for a key k if
and only if m.containsKey(k) would
return true.)

Parameters:
key - key with which the specified value is to be associated.
value - value to be associated with the specified key.

Returns:
previous value associated with specified key, or null if there was no
mapping for key. (A null return can also indicate that the map previously associated null with the specified key, if the implementation supports null values.)

因此,如果在调用mymap.put("1","a string")时没有分配返回的值,它就会变为未引用,因此有资格进行垃圾回收。


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

如果您希望保留所有值,则可以考虑实现以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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"的值时,如果您希望获得其中一个值属于输入键的实际值,那么就是它?!这是为每个值都有唯一键的原因,但你可以通过java标准库获得一个技巧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);
    }
}

你可以用这种方式使用它:

1
2
3
4
5
6
7
8
9
    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));
}

打印结果如下:

1
2
3
[one, not one, surely not one]
not one
null


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


对于你的问题,地图是否像一个桶:没有。

它就像一个name=value对的列表,而name不需要是一个字符串(尽管如此)。

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

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

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

如果您想了解更多相关信息,可以查看javapractices.com和technofundo.com上的文章。

问候


它替换了相应键的映射中的现有值。如果没有具有相同名称的密钥,则它会创建一个具有所提供值的密钥。
例如:

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

OUTPUT
key ="1",value ="two"

因此,之前的值会被覆盖。


我一直用:

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

如果我想将多个东西应用于一个识别键。

1
2
3
4
5
6
7
8
9
10
11
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);
    }

}

你可以做这样的事情,创造一个迷宫!

1
2
3
4
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");
}

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

  • 最好的新值将覆盖以前的值。

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

没有重复:

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

Ok. You will get: $2 ==> {one=one}

重复流:

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

Exception java.lang.IllegalStateException: Duplicate key 1 (attempted merging values one and not one)
| at Collectors.duplicateKeyException (Collectors.java:133)
| at Collectors.lambda$uniqKeysMapAccumulator$1 (Collectors.java:180)
| at ReduceOps$3ReducingSink.accept (ReduceOps.java:169)
| at Spliterators$ArraySpliterator.forEachRemaining (Spliterators.java:948)
| at AbstractPipeline.copyInto (AbstractPipeline.java:484)
| at AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:474)
| at ReduceOps$ReduceOp.evaluateSequential (ReduceOps.java:913)
| at AbstractPipeline.evaluate (AbstractPipeline.java:234)
| at ReferencePipeline.collect (ReferencePipeline.java:578)
| at (#4:1)

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

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

祝好运! :)


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

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


顺便说一句,如果你想要一些语义,比如只有当这个键不存在时才放。您可以使用concurrentHashMapputIfAbsent()功能。
看一下这个:

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

concurrentHashMap具有高性能的线程安全性,因为它使用"锁定条带化"机制来提高吞吐量。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
         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,请参阅源文档

1
2
3
4
5
public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}