目录
map 中雷同的 key 保留多个 value 值
如下代码
Map 中雷同的键 Key 不同的值 Value 实现原理
实现原理
总结
map 中雷同的 key 保留多个 value 值
在 java 中,Map 汇合中只能保留一个雷同的 key,如果再增加雷同的 key,则之后增加的 key 的值会笼罩之前 key 对应的值,Map 中一个 key 只存在惟一的值。
如下代码
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。
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 的外部类,其定义形式为:
static class Entry<K, V> extends HashMap.Node<K, V> {Entry<K, V> before; Entry<K, V> after;}
总结
IdentityHashMap 和 LinkedMultiValueMap 的实现归根结底就是数组和链表的应用。
以上为集体教训,心愿能给大家一个参考