日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Map、HashMap、TreeMap、LinkedHashMap

發(fā)布時間:2025/4/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Map、HashMap、TreeMap、LinkedHashMap 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Map
與Set接口一樣,Map也是一個接口不能實例化
Map是一種鍵(key)-值(value)對集合,Map中的每個元素都是一個鍵值對,其中key只能有一個為null且key不能重復(唯一),而value可以有多個為null且value可以重復(不唯一),當key值重復寫入時,新寫入的value值會覆蓋原有的值。
Map提供的是一種映射關系,能夠?qū)崿F(xiàn)通過key快速的查找value

HashMap

  • 底層數(shù)據(jù)結(jié)構(gòu):數(shù)組+鏈表+紅黑樹實現(xiàn)(JDK1.8之后,JDK1.8之前是數(shù)組+鏈表):具體實現(xiàn)見底部:-)(哈希表)
  • 是否有序:無序
  • 是否排序:否
  • 是否允許存入null:允許:且null作為key時,總是存儲在hashtable第一個節(jié)點上,但最好不要這樣用,出現(xiàn)問題的話,排查比較麻煩。
  • 是否線程安全:線程不安全
  • 如何解決線程不安全的問題:(1).多線程環(huán)境中推薦使用ConcurrentHashMap (2).在多線程環(huán)境下若使用HashMap需要使用Collections.synchronizedMap()方法來獲取一個線程安全的集合
    來,上個代碼
public static void main(String[] args) {HashMap<String, Integer> hm=new HashMap<>();hm.put("張三豐", 1);hm.put("張三豐", 12);hm.put(null, 123);hm.put("張無忌", null);hm.put("張捕頭", null);//注意注意,開始輸出啦//1.直接輸出System.out.println("直接輸出:");System.out.println(hm);//2.使用迭代器輸出Iterator<Entry<String, Integer>> iter=hm.entrySet().iterator();System.out.println("使用迭代器輸出:");while(iter.hasNext()) {Map.Entry<String, Integer> entry=iter.next();System.out.print("key:"+entry.getKey()+" "+"value:"+entry.getValue()+"--");}System.out.println();//3.使用keySet遍歷key,在通過key去除valueSystem.out.println("keySet遍歷key:");for (String key : hm.keySet()) {System.out.print("key:"+key+" "+"value:"+hm.get(key)+"--");}System.out.println();//4.不使用迭代器,直接使用Entry輸出System.out.println("不使用迭代器,直接使用Entry輸出:");for (Entry<String, Integer> entry : hm.entrySet()) {System.out.print("key:"+entry.getKey()+" "+"value:"+entry.getValue()+"--");}}

測試結(jié)果

直接輸出: {null=123, 張三豐=12, 張無忌=null, 張捕頭=null} 使用迭代器輸出: key:null value:123--key:張三豐 value:12--key:張無忌 value:null--key:張捕頭 value:null-- keySet遍歷key: key:null value:123--key:張三豐 value:12--key:張無忌 value:null--key:張捕頭 value:null-- 不使用迭代器,直接使用Entry輸出: key:null value:123--key:張三豐 value:12--key:張無忌 value:null--key:張捕頭 value:null--

TreeMap

  • 底層數(shù)據(jù)結(jié)構(gòu):紅黑樹
  • 是否有序:無序
  • 是否排序:是,按照key排序
  • key能否為null:不能,因為沒有null的比較方法,但value可以為null
  • 是否線程安全:線程不安全
    基于紅黑樹實現(xiàn)。TreeMap沒有調(diào)優(yōu)選項,因為該樹總處于平衡狀態(tài)。
    基于紅黑二叉樹的NavigableMap的實現(xiàn),存入TreeMap的元素應當實現(xiàn)Comparable接口或者實現(xiàn)Comparator接口,會按照排序后的順序迭代元素,兩個相比較的key不得拋出classCastException。主要用于存入元素的時候?qū)υ剡M行自動排序,迭代輸出的時候就按排序順序輸出
    來個代碼
/*這段代碼是牛客網(wǎng)上的一道編程題,百度“牛牛找工作”即可看到原題*/ package other_demo;import java.util.Scanner; import java.util.TreeMap;public class test_demo {public static void main(String[] args) {int N=0,M=0;Scanner sc=new Scanner(System.in);M=sc.nextInt();//獲取小伙伴的人數(shù)N=sc.nextInt();//獲取工作數(shù)量int[] Ai=new int[M];//用于存儲小伙伴的能力值TreeMap<Integer, Integer> job=new TreeMap<>();int key=0,value=0;for (int i = 0; i < N; i++) {key=sc.nextInt();value=sc.nextInt();if(job.containsKey(key)) {//保證在key相同時,value一直是最大的value=job.get(key)>value?job.get(key):value;}job.put(key, value);}key=job.firstKey();//獲取第一個keyint lastkey=job.lastKey();//獲取最大的keyint temp1,temp2;//分別用來保存兩個相鄰key的value用于比較大小int inter;//中間人,用來取下一個key值//因為是在能力值范圍內(nèi)取最大報酬,所以把報酬重排序while (key!=lastkey) {inter=job.higherKey(key);//獲取key的下一個keytemp1=job.get(key);temp2=job.get(inter);value=temp1>temp2?temp1:temp2;job.put(inter, value);key=inter;}//獲取能力值 // Iterator<Integer> iter=job.keySet().iterator();for (int i = 0; i < M; i++) {Ai[i]=sc.nextInt();System.out.println(job.floorEntry(Ai[i]).getValue());}} }

再加盤代碼吧

public static void main(String[] args) {TreeMap<Integer, String> tm=new TreeMap<>();tm.put(3, "王");tm.put(1, "孟");tm.put(2, "李");System.out.println("直接輸出"+tm);System.out.println("firstKey():"+"key="+tm.firstKey()+" value="+tm.get(tm.firstKey()));System.out.println("higherKey():"+"key="+tm.higherKey(tm.firstKey())+" value="+tm.get(tm.higherKey(tm.firstKey())));System.out.println("subMap():"+tm.subMap(1, 3));System.out.println("subMap():"+tm.subMap(1, false, 3, true));System.out.println("ceilingKey():"+tm.ceilingKey(2));System.out.println("ceilingEntry():"+tm.ceilingEntry(2));System.out.println("higherEntry():"+tm.higherEntry(2));System.out.println("headMap():"+tm.headMap(2));//排序后,輸出從firstkey開始到secondkey之間的所有key-value對,不包括secondkey;System.out.println("headMap():"+tm.headMap(2,true));System.out.println("tailMap():"+tm.tailMap(2));System.out.println("tailMap():"+tm.tailMap(2, false));//若不加第二個參數(shù),默認為true;System.out.println("descendingKeySet():"+tm.descendingKeySet());//倒序輸出TreeMap的key值System.out.println("descendingMap():"+tm.descendingMap());//倒序輸出TreeMap的key-valueSystem.out.println("containsKey():"+tm.containsKey(2));System.out.println("containsValue():"+tm.containsValue("李"));} 直接輸出{1=, 2=, 3=} firstKey():key=1 value=higherKey():key=2 value=subMap():{1=, 2=} subMap():{2=, 3=} ceilingKey():2 ceilingEntry():2=higherEntry():3=headMap():{1=} headMap():{1=, 2=} tailMap():{2=, 3=} tailMap():{3=} descendingKeySet():[3, 2, 1] descendingMap(){3=, 2=, 1=} containsKey():true containsValue()true

key=iter.next();可以獲取key的值
分析:雖然使用keyset及entryset來進行遍歷能取得相同的結(jié)果,
但兩者的遍歷速度是有差別的。
keySet():迭代后只能通過get()取key;再根據(jù)key值取value。
entrySet():迭代后可以e.getKey(),e.getValue()取key和value。

說明:keySet()的速度比entrySet()慢了很多,也就是keySet方式遍歷Map的性能不如entrySet性能好
為了提高性能,以后多考慮用entrySet()方式來進行遍歷。

LinkedHashMap

  • 底層數(shù)據(jù)結(jié)構(gòu):雙向鏈表
  • 是否有序:有序
  • 是否排序:不排序
  • 是否允許鍵值對為空:允許有一個鍵為空,多個值為空
  • 是否線程安全:線程不安全
  • 是否允許重復的key值:不允許,會覆蓋掉原key的value

上個線程代碼吧

public static void main(String[] args) {LinkedHashMap<String, String> lhm=new LinkedHashMap<>();for (int i = 0; i <=30; i++) {new Thread(()->{lhm.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 8));System.out.println(lhm);},String.valueOf(i)).start();}}

測試結(jié)果

java.util.ConcurrentModificationException

HashTable

  • 底層數(shù)據(jù)結(jié)構(gòu):JDK1.8之前是數(shù)組+鏈表,JDK1.8之后是數(shù)組+鏈表+紅黑樹
  • 是否有序:無序
  • 是否排序:否
  • 是否線程安全:是
  • 是否允許鍵值對為null:不允許
  • 是否允許有重復的元素:不允許,重復的key所對應的value會覆蓋原本的value。

從源碼上看:
HashTable繼承Dictionary,實現(xiàn)了Map接口

public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable {

照例來盤代碼:
又好像沒什么必要吧,畢竟用法和HashMap一樣,多了個線程安全

public static void main(String[] args) {Hashtable<String, String> ht=new Hashtable<>();for (int i = 0; i <=30; i++) {new Thread (()->{ht.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 8));System.out.println(ht);},String.valueOf(i)).start();}}

數(shù)組+鏈表+紅黑樹:
如下圖所示,就是這樣滴:

寫一些大致過程吧:以HashMap<String,Integer>為例:首先,你開始了put("張",1);
而put的源碼是:

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}

這里要注意hash(key)是計算你傳入的key的哈希值,然后調(diào)用putVal()函數(shù)開始存儲。
進入putVal()后會經(jīng)過一系列判斷;比如新加入的key的HashCode值所對應的位置在Entry數(shù)組中是否已經(jīng)存儲了別的值。如果沒有,直接寫入。如果有,則判斷key是否相等,不相等的話再繼續(xù)判斷next是否有值,有值的話,還需要知道是紅黑樹還是鏈表,然后再根據(jù)各自的方法判斷添加。如果是鏈表,還需要在添加完成后判斷是否需要轉(zhuǎn)成紅黑樹。如果上面這些都沒有,而是直接添加在Entry數(shù)組中的話,需要判斷一下是否要擴容。
具體源碼分析可以看這篇博客:href="https://blog.csdn.net/m0_37914588/article/details/82287191

總結(jié)

以上是生活随笔為你收集整理的Map、HashMap、TreeMap、LinkedHashMap的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。