java map中相同的key保存多個(gè)value值方式
map中相同的key保存多個(gè)value值
在java中,Map集合中只能保存一個(gè)相同的key,如果再添加相同的key,則之后添加的key的值會(huì)覆蓋之前key對(duì)應(yīng)的值,Map中一個(gè)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");//會(huì)覆蓋之前的值,map長(zhǎng)度為1
/**
* map比較鍵是否相同時(shí)是根據(jù)hashCode()和equals()兩個(gè)方法進(jìn)行比較
* 先比較hashCode()是否相等,再比較equals()是否相等(實(shí)際上就是比較對(duì)象是否相等),如果都相等則認(rèn)定是同一個(gè)鍵
*/
for(Map.Entry<String,String> entry:map.entrySet()){
System.out.println(entry.getKey()+" "+entry.getValue());
}
System.out.println("------->"+map.get("key"));
}
控制臺(tái)輸出如下:
/**
* 以上代碼可以看出普通的map集合相同的key只能保存一個(gè)value
* 但是有一個(gè)特殊的map--->IdentityHashMap可以實(shí)現(xiàn)一個(gè)key保存多個(gè)value
* 注意:此類并不是通用的Map實(shí)現(xiàn)!此類再實(shí)現(xiàn)Map接口的時(shí)候違反了Map的常規(guī)協(xié)定,Map的常規(guī)協(xié)議在
* 比較對(duì)象強(qiáng)制使用了equals()方法,但此類設(shè)計(jì)僅用于其中需要引用相等性語義的情況
* (IdentityhashMap類利用哈希表實(shí)現(xiàn)Map接口,比較鍵(和值)時(shí)使用引用相等性代替對(duì)象相等性,
* 也就是說做key(value)比較的時(shí)候只比較兩個(gè)key是否引用同一個(gè)對(duì)象)
*/
@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"));
}
控制臺(tái)輸出如下
/**
* test1中的IdentityHashMap中的key為“key”還是只保存了一個(gè)值,以為“key”在內(nèi)存中只存在一個(gè)對(duì)象,
* 而str1與str2對(duì)對(duì)"key"字符串的引用是相等的,所以添加的時(shí)候就發(fā)生了覆蓋
*/
@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));
}
控制臺(tái)輸出如下:
/**
* test2中str1,str2都在內(nèi)存中指向不同的String對(duì)象,他們的哈希值是不同的,所以在identityHashMap中可以的比較
* 中會(huì)認(rèn)為不同的key,所以會(huì)存在相同的“key”值對(duì)應(yīng)不同的value值
*/
/**
* 既然提到了map的key的比較,再說一下map中實(shí)現(xiàn)自定義類做key值時(shí)應(yīng)該注意的一些細(xì)節(jié),
* 在HashMap中對(duì)于key的比較時(shí)通過兩步完成的
* 第一步:計(jì)算對(duì)象的hash Code的值,比較是否相等
* 第二步: 檢查對(duì)應(yīng)的hash code對(duì)應(yīng)位置的對(duì)象是否相等
* 在第一步中會(huì)調(diào)用到對(duì)象中的hashCode()方法,第二步中會(huì)調(diào)用的對(duì)象中的equals()方法
*
* 所以想要實(shí)現(xiàn)自定義對(duì)象作為Map的key值,保證key值的唯一性,需要在子定義對(duì)象中重寫以上兩個(gè)方法,如以下對(duì)象:
*/
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實(shí)現(xiàn)原理
Map中相同的鍵Key對(duì)應(yīng)不同的值Value通常出現(xiàn)在樹形結(jié)構(gòu)的數(shù)據(jù)處理中,通常的實(shí)現(xiàn)方法有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));
}
}
實(shí)現(xiàn)原理
- JDK提供的IdentityHashMap其底層是根據(jù)Key的hash碼的不同+transient Object[] table來實(shí)現(xiàn)的;
- Spring提供的LinkedMultiValueMap其底層是使用LinkedHashMap來實(shí)現(xiàn)的;
- LinkedHashMap的底層是使用transient Entry<K, V> head和transient Entry<K, V> tail來實(shí)現(xiàn)的;
- Entry是LinkedHashMap的內(nèi)部類,其定義方式為:
static class Entry<K, V> extends HashMap.Node<K, V> { Entry<K, V> before; Entry<K, V> after; }
總結(jié)
IdentityHashMap和LinkedMultiValueMap的實(shí)現(xiàn)歸根結(jié)底就是數(shù)組和鏈表的使用。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot 中常用的注解@RequestParam及基本用法
@RequestParam 是 Spring Framework 和 Spring Boot 中常用的注解之一,用于從請(qǐng)求中獲取參數(shù)值,本文給大家介紹Spring Boot 中常用的注解@RequestParam,感興趣的朋友一起看看吧2023-10-10
使用maven一步一步構(gòu)建spring mvc項(xiàng)目(圖文詳解)
這篇文章主要介紹了詳解使用maven一步一步構(gòu)建spring mvc項(xiàng)目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Java實(shí)現(xiàn)二叉樹的建立、計(jì)算高度與遞歸輸出操作示例
這篇文章主要介紹了Java實(shí)現(xiàn)二叉樹的建立、計(jì)算高度與遞歸輸出操作,結(jié)合實(shí)例形式分析了Java二叉樹的創(chuàng)建、遍歷、計(jì)算等相關(guān)算法實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-03-03
Spring-AOP自動(dòng)創(chuàng)建代理之BeanNameAutoProxyCreator實(shí)例
這篇文章主要介紹了Spring-AOP自動(dòng)創(chuàng)建代理之BeanNameAutoProxyCreator實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
解決MyBatis中為類配置別名,列名與屬性名不對(duì)應(yīng)的問題
這篇文章主要介紹了解決MyBatis中為類配置別名,列名與屬性名不對(duì)應(yīng)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-11-11
Springboot+SpringSecurity實(shí)現(xiàn)圖片驗(yàn)證碼登錄的示例
本文主要介紹了Springboot+SpringSecurity實(shí)現(xiàn)圖片驗(yàn)證碼登錄的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04

