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://zxbcw.cn/post/221077/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)