服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - java map中相同的key保存多个value值方式

java map中相同的key保存多个value值方式

2021-12-08 11:57成都杨小天 Java教程

这篇文章主要介绍了java map中相同的key保存多个value值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

map中相同的key保存多个value值

在java中,Map集合中只能保存一个相同的key,如果再添加相同的key,则之后添加的key的值会覆盖之前key对应的值,Map中一个key只存在唯一的值。

如下代码

?
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package test;
import org.junit.Test;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import static java.util.Objects.hash;
public class HashMapTest {
 
    @Test
    public void test0() {
        String str1 = new String("key");
        String str2 = new String("key");
        System.out.println(str1 == str2);
 
        Map<String,String> map = new HashMap<String,String>();
        map.put(str1,"value1");
        map.put(str2,"value2");//会覆盖之前的值,map长度为1
        /**
         * map比较键是否相同时是根据hashCode()和equals()两个方法进行比较
         * 先比较hashCode()是否相等,再比较equals()是否相等(实际上就是比较对象是否相等),如果都相等则认定是同一个键
         */
 
        for(Map.Entry<String,String> entry:map.entrySet()){
            System.out.println(entry.getKey()+"  "+entry.getValue());
        }
        System.out.println("------->"+map.get("key"));
    }
控制台输出如下:
 
    /**
     * 以上代码可以看出普通的map集合相同的key只能保存一个value
     * 但是有一个特殊的map--->IdentityHashMap可以实现一个key保存多个value
     * 注意:此类并不是通用的Map实现!此类再实现Map接口的时候违反了Map的常规协定,Map的常规协议在
     * 比较对象强制使用了equals()方法,但此类设计仅用于其中需要引用相等性语义的情况
     * (IdentityhashMap类利用哈希表实现Map接口,比较键(和值)时使用引用相等性代替对象相等性,
     * 也就是说做key(value)比较的时候只比较两个key是否引用同一个对象)
     */
    @Test
    public void test1(){
        String str1 = "key";
        String str2 = "key";
        System.out.println(str1 == str2);
        Map<String,String> map = new IdentityHashMap<>();
        map.put(str1,"value1");
        map.put(str2,"value2");
        for(Map.Entry<String,String> entry:map.entrySet()){
            System.out.println(entry.getKey()+"  "+entry.getValue());
        }
        System.out.println("containsKey---->"+map.get("key"));
        System.out.println("value---->"+map.get("key"));
    }
 控制台输出如下
 
 /**
     * test1中的IdentityHashMap中的key为“key”还是只保存了一个值,以为“key”在内存中只存在一个对象,
     * 而str1与str2对对"key"字符串的引用是相等的,所以添加的时候就发生了覆盖
     */
 
    @Test
    public void test2(){
        String str1 = new String("key");
        String str2 = new String("key");
        System.out.println(str1 == str2);
        Map<String, String> map = new IdentityHashMap<>();
        map.put(str1,"value1");
        map.put(str2,"value2");
        for(Map.Entry<String,String> entry:map.entrySet()){
            System.out.println(entry.getKey()+"  "+entry.getValue());
        }
        System.out.println("\"key\" containKey--->"+map.containsKey("key"));
        System.out.println("str1 containKey--->"+map.containsKey(str1));
        System.out.println("str2 containKey--->"+map.containsKey(str2));
        System.out.println("value--->"+map.get("key"));
        System.out.println("value--->"+map.get(str1));
        System.out.println("value--->"+map.get(str2));
    }
 控制台输出如下:
 
 /**
     * test2中str1,str2都在内存中指向不同的String对象,他们的哈希值是不同的,所以在identityHashMap中可以的比较
     * 中会认为不同的key,所以会存在相同的“key”值对应不同的value值
     */
 
    /**
     * 既然提到了map的key的比较,再说一下map中实现自定义类做key值时应该注意的一些细节,
     * 在HashMap中对于key的比较时通过两步完成的
     *  第一步:计算对象的hash Code的值,比较是否相等
     *  第二步: 检查对应的hash code对应位置的对象是否相等
     *  在第一步中会调用到对象中的hashCode()方法,第二步中会调用的对象中的equals()方法
     *
     *  所以想要实现自定义对象作为Map的key值,保证key值的唯一性,需要在子定义对象中重写以上两个方法,如以下对象:
     */
    private class CustomObject{
        private String value;
        public CustomObject(String value){
            this.value = value;
        }
 
        public String getValue() {
            return value;
        }
 
        public void setValue(String value) {
            this.value = value;
        }
 
        /**
         * 省略自定义的一些属性方法
         * ......
         */
 
        @Override
        public int hashCode() {
            if(value !=null){
                return super.hashCode()+hash(value);
            }else{
                return super.hashCode();
            }
        }
 
        @Override
        public boolean equals(Object obj) {
            if(this == obj){
                return true;
            }
            if(obj == null || getClass() != obj.getClass()){
                return false;
            }
            CustomObject object = (CustomObject) obj;
            if(this.value != null && this.value.equals(object.getValue())){
                return true;
            }
            if(this.value == null && object.value == null){
                return true;
            }
            return false;
        }
    }
}

Map中相同的键Key不同的值Value实现原理

Map中相同的键Key对应不同的值Value通常出现在树形结构的数据处理中,通常的实现方法有JDK提供的IdentityHashMap和Spring提供的MultiValueMap。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
    Map<String, Object> identity = new IdentityHashMap<>();
    identity.put("A", "A");
    identity.put("A", "B");
    identity.put("A", "C");
    Map<String, Object> identityString = new IdentityHashMap<>();
    identityString.put(String.join("A", ""), "B");
    identityString.put("A", "A");
    identityString.put(new String("A"), "C");
    MultiValueMap<String, Object> linked = new LinkedMultiValueMap<>();
    linked.add("A", "A");
    linked.add("A", "B");
    linked.add("A", "C");
    for (String key : identity.keySet()) {
        System.out.println("identity:" + identity.get(key));
    }
    for (String key : identityString.keySet()) {
        System.out.println("identity string:" + identityString.get(key));
    }
    for (String key : linked.keySet()) {
        System.out.println("linked:" + linked.get(key));
    }
}

实现原理

  • JDK提供的IdentityHashMap其底层是根据Key的hash码的不同+transient Object[] table来实现的;
  • Spring提供的LinkedMultiValueMap其底层是使用LinkedHashMap来实现的;
  • LinkedHashMap的底层是使用transient Entry<K, V> head和transient Entry<K, V> tail来实现的;
  • Entry是LinkedHashMap的内部类,其定义方式为:
?
1
static class Entry<K, V> extends HashMap.Node<K, V> { Entry<K, V> before; Entry<K, V> after; }

总结

IdentityHashMap和LinkedMultiValueMap的实现归根结底就是数组和链表的使用。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/weixin_37774620/article/details/79128212

延伸 · 阅读

精彩推荐
  • Java教程浅谈springBoot注解大全

    浅谈springBoot注解大全

    本篇文章主要介绍了浅谈springBoot注解大全,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    tanwei8112422021-04-13
  • Java教程Java实现双向链表(两个版本)

    Java实现双向链表(两个版本)

    这篇文章主要介绍了Java实现双向链表(两个版本)的相关资料,需要的朋友可以参考下 ...

    MRR4452020-03-28
  • Java教程Java程序与C语言的区别浅析

    Java程序与C语言的区别浅析

    Java和C语言虽有相同性,但两者也有一定的不同。Java程序是面向对象的一种简单、分布式 、解释、健壮、安全、结构中立、可移植、高效能、多线程、动态...

    孟月2922020-09-06
  • Java教程Java数组的基本操作方法资料整理

    Java数组的基本操作方法资料整理

    这篇文章主要给大家介绍了关于Java中数组的定义和使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    Sleepinglion10642021-09-14
  • Java教程SpringCloud Ribbon 负载均衡的实现

    SpringCloud Ribbon 负载均衡的实现

    Ribbon是一个客户端负载均衡器,它提供了对HTTP和TCP客户端的行为的大量控制。这篇文章主要介绍了SpringCloud Ribbon 负载均衡的实现,感兴趣的小伙伴们可以...

    huanzi-qch12442021-07-01
  • Java教程Java绘图技术的详解及实例

    Java绘图技术的详解及实例

    这篇文章主要介绍了Java绘图技术的详解及实例的相关资料,这里主要详解Graphics类的使用,需要的朋友可以参考下...

    cxq11265452020-12-11
  • Java教程java实现会反弹的小球示例

    java实现会反弹的小球示例

    这篇文章主要介绍了java实现会反弹的小球示例,需要的朋友可以参考下 ...

    Java教程网2262019-11-21
  • Java教程深入理解JVM自动内存管理

    深入理解JVM自动内存管理

    对于Java虚拟机在内存分配与回收的学习,本文主要介绍了JVM自动内存管理,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习...

    爱奇志9962021-11-29