基于java HashMap插入重復(fù)Key值問(wèn)題
java HashMap插入重復(fù)Key值
要在HashMap中插入重復(fù)的值,首先需要弄清楚HashMap里面是怎么存放元素的。
put方法
Map里面存放的每一個(gè)元素都是key-value這樣的鍵值對(duì),而且都是通過(guò)put方法進(jìn)行添加的,而且相同的key在Map中只會(huì)有一個(gè)與之關(guān)聯(lián)的value存在。put方法在Map中的定義如下。
V put(K key, V value);
put()方法實(shí)現(xiàn):
首先hash(key)得到key的hashcode(),hashmap根據(jù)獲得的hashcode找到要插入的位置所在的鏈,在這個(gè)鏈里面放的都是hashcode相同的Entry鍵值對(duì),在找到這個(gè)鏈之后,會(huì)通過(guò)equals()方法判斷是否已經(jīng)存在要插入的鍵值對(duì),而這個(gè)equals比較的其實(shí)就是key。
它用來(lái)存放key-value這樣的一個(gè)鍵值對(duì),返回值是key在Map中存放的舊value,如果之前不存在則返回null。HashMap的put方法是這樣實(shí)現(xiàn)的。
// 在此映射中關(guān)聯(lián)指定值與指定鍵。如果該映射以前包含了一個(gè)該鍵的映射關(guān)系,則舊值被替換 public V put(K key, V value) { ? ? // 當(dāng)key為null,調(diào)用putForNullKey方法,保存null與table第一個(gè)位置中,這是HashMap允許為null的原因? ? ? if (key == null) ? ? ? ? return putForNullKey(value); ? ? // 使用hash函數(shù)預(yù)處理hashCode,計(jì)算key的hash值 ? ? ? int hash = hash(key.hashCode());//-------(1) ? ? // 計(jì)算key hash 值在 table 數(shù)組中的位置? ? ? int i = indexFor(hash, table.length);//------(2) ? ? // 從i出開始迭代 e,找到 key 保存的位置 ? ? for (Entry<K, V> e = table[i]; e != null; e = e.next) { ? ? ? ? Object k; ? ? ? ? // 判斷該條鏈上是否有hash值相同的(key相同)? ? ? ? ? // 若存在相同,則直接覆蓋value,返回舊value? ? ? ? ? if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { ? ? ? ? ? ? // 舊值 = 新值 ? ? ? ? ? ? ? V oldValue = e.value; ? ? ? ? ? ? // 將要存儲(chǔ)的value存進(jìn)去 ? ? ? ? ? ? e.value = value; ? ? ? ? ? ? e.recordAccess(this); ? ? ? ? ? ? // 返回舊的value ? ? ? ? ? ? return oldValue; ? ? ? ? } ? ? } ? ? // 修改次數(shù)增加1? ? ? modCount++; ? ? // 將key、value添加至i位置處? ? ? addEntry(hash, key, value, i); ? ? return null; }
從上我們可以看到在添加對(duì)應(yīng)的key-value這樣的組合時(shí),如果原本已經(jīng)存在對(duì)應(yīng)的key,則直接改變對(duì)應(yīng)的value,并返回舊的value,而在判斷key是否存在的時(shí)候是先比較key的hashCode,再比較相等或equals的。
直接從上面代碼來(lái)看是比較的對(duì)應(yīng)Map.Entry的hashCode和key的hashCode,而實(shí)際上Map.Entry的hashCode其實(shí)就是其存放key的hashCode。
而如果對(duì)應(yīng)的key原本不存在的話將調(diào)用addEntry將對(duì)應(yīng)的key-value添加到Map中。
addEntry傳遞的參數(shù)hash就是對(duì)應(yīng)key的hashCode。
實(shí)現(xiàn)引用對(duì)象作為keys的唯一性
通過(guò)對(duì)put()方法的研究,我們可以發(fā)現(xiàn),判斷key是否存在的時(shí)候是先比較key的hashCode,再比較相等或equals的,所以重寫hashCode()和equals()方法即可實(shí)現(xiàn)覆蓋keys的引用(指向具有相同實(shí)例變量的對(duì)象)。
class MyType { ? ? private String arga; ? ? private String argb; ? ? public MyType(String arga, String argb) { ? ? ? ? this.arga = arga; ? ? ? ? this.argb = argb; ?? ?} ?? ?@Override ?? ?public int hashCode(){ ? ? ? ? ? ? ? ?? ?? ? ? ? return this.arga.hashCode() * this.argb.hashCode() ;? ?? ?}? ?? ? ?? ?@Override ?? ?public boolean equals(Object obj) { ?? ?? ? ? ?if (this == obj) { ? ? ? ? ? ? ?? ?? ? ? ? ? ?return true; ? ? ? ? ? ? ? ? ? ?? ? ? ?} ? ? ? ?? ?? ? ? ?if (!(obj instanceof MyType)) { ? ?? ? ? ? ? ?return false; ? ? ? ? ? ? ?? ?? ? ? ?} ? ? ?? ??? ?MyType p = (MyType) obj; ? ?? ??? ?if (this.arga.equals(p.arga) && this.argb.equals(p.argb)) { ? ? ? ? ? ? ? ?? ? ? ? ? ?return true ; ? ? ? ? ? ? ? ? ? ?? ? ? ?} else { ? ? ? ? ?? ?? ? ? ? ? ?return false ; ? ? ? ? ? ? ? ? ?? ? ? ?} ? ? ?? ?? ?} }
重寫這兩個(gè)方法之后就可以覆蓋重復(fù)的引用對(duì)象,如果需要對(duì)value進(jìn)行疊加,調(diào)用put()方法之前用containsKey()方法判斷是否有重復(fù)的鍵值,如果有,則用get()方法獲取原有的value,再加上新加入的value即可。
HashMap解決key值相同問(wèn)題
某些場(chǎng)景需要一個(gè)key值下面對(duì)應(yīng)多個(gè)值,但是map的一個(gè)key值只對(duì)應(yīng)一個(gè)value值,由于hashmap相同的key值,第二個(gè)put進(jìn)去會(huì)覆蓋第一個(gè)的值,所以為了解決這一問(wèn)題:所以用list存
如下:
List<Map<String, List<RecommendationListBO>>> hashList = new ArrayList<>(); Iterator<Map.Entry<String, List<RecommendationListBO>>> iterator = recommendationHashMap.entrySet().iterator(); Map.Entry<String, List<RecommendationListBO>> entry; while (iterator.hasNext()) { ? ? entry = iterator.next(); ? ? // 往newMap中放入新的Entry ? ? HashMap<String, List<RecommendationListBO>> newMap = new LinkedHashMap<>(); ? ? newMap.put(entry.getKey().split(",")[0], entry.getValue()); ? ? hashList.add(newMap); }
每次new一個(gè)新的map,add到map的list里面。思路大概是這樣的。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Tree組件實(shí)現(xiàn)支持50W數(shù)據(jù)方法剖析
這篇文章主要為大家介紹了Tree組件實(shí)現(xiàn)支持50W數(shù)據(jù)的方法剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08學(xué)生視角看Java 面向?qū)ο蟮睦^承本質(zhì)
繼承是java面向?qū)ο缶幊碳夹g(shù)的一塊基石,因?yàn)樗试S創(chuàng)建分等級(jí)層次的類。繼承就是子類繼承父類的特征和行為,使得子類對(duì)象(實(shí)例)具有父類的實(shí)例域和方法,或子類從父類繼承方法,使得子類具有父類相同的行為2022-03-03解決SpringBoot在后臺(tái)接收前臺(tái)傳遞對(duì)象方式的問(wèn)題
這篇文章主要介紹了解決SpringBoot在后臺(tái)接收前臺(tái)傳遞對(duì)象方式的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01java8實(shí)現(xiàn)list集合中按照某一個(gè)值相加求和,平均值等操作代碼
這篇文章主要介紹了java8實(shí)現(xiàn)list集合中按照某一個(gè)值相加求和,平均值等操作代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08快速解決idea打開某個(gè)項(xiàng)目卡住的問(wèn)題
這篇文章主要介紹了解決idea打開某個(gè)項(xiàng)目卡住的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Spring中@Async用法詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了Spring中@Async用法詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-02-02Java實(shí)現(xiàn)注冊(cè)登錄與郵箱發(fā)送賬號(hào)驗(yàn)證激活功能
這篇文章主要介紹了Java實(shí)現(xiàn)注冊(cè)登錄與郵箱發(fā)送賬號(hào)驗(yàn)證激活功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-12-12Mybatis使用useGeneratedKeys獲取自增主鍵
這篇文章主要為大家介紹了Mybatis使用useGeneratedKeys獲取自增主鍵示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01在SpringBoot中使用lombok的注意事項(xiàng)
這篇文章主要介紹了在SpringBoot中使用lombok的注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12