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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java集合大整理

發(fā)布時間:2025/4/5 java 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java集合大整理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

此處為整理,更詳細的源碼分析請查閱?JDK源碼分析其他文章

為了適應csdn的窗口大小,表格嚴重變形了。。。


null 值重復 底層實現(xiàn) 擴容 增、刪、迭代 包含 備注
HashSet 允許,just 1個 no HashMap 同HashMap 【add】:調(diào)用HashMap的put方法,put的value傳入偽值static final?Object?PRESENT?=?new?Object(),僅僅為了保持映射關(guān)系;(所有value都是同一個對象) 【remove】:調(diào)map的remove 有contains, 無get HashMap中的Key是根據(jù)對象的hashCode() 和 euqals()來判斷是否唯一的。 So:為了保證HashSet中的對象不會出現(xiàn)重復值,在被存放元素的類中必須要重寫hashCode()和equals()這兩個方法。
TreeSet No,
add-null空指針異常
no 【TreeMap】, 實現(xiàn)了NavigableMap接口, 一種SortedMap 樹結(jié)構(gòu),無擴容一說 add調(diào)用TreeMap的put方法,同樣有PRESENT remove方法。 有contains, 無get
默認使用元素的自然順序?qū)υ剡M行排序; 可重寫compare方法實現(xiàn)對象的自定義排序。
EnumSet No,
add-null空指針異常
no long(數(shù)組)、 位運算 Enum元素固定,無擴容 add:數(shù)組賦值; remove:置null 有contains, 無get
判斷是否包含null和removenull無異常;remove-null時返回false
EnumMap No, add-null空指針異常; value可以為null key不重復 transient數(shù)組 Enum元素固定,無擴容
put:數(shù)組賦值; remove:置null containsKey; containsValue。 創(chuàng)建時必須指定key類型; 元素順序為Enum的順序; 迭代時不會拋出ConcurrentModificationException; NULL和null的區(qū)別。
HashMap
key、value均允許null, 但key僅允許1個null。
key不重復, value可以
位桶+鏈表/紅黑樹 size?>?tab.length*threshold; newCap = oldCap<<1; 新容量:2倍擴容 put、remove; 迭代時remove拋ConcurrentModificationException;注意正確迭代方式 containsKey; containsValue。

LinkedHashMap 同HashMap 同HashMap
HashMap+雙向鏈表 同HashMap
put、remove;
注意get模式; contains調(diào)用HashMap的containsKey;
containsValue(遍歷鏈表)
像hashMap一樣用table儲存元素【桶位依舊分散,和HashMap的存放位置相同】,put時直接調(diào)用的是HashMap的put方法。
TreeMap Key不允許null; value允許。 同HashMap





ArrayList 允許null,隨意 允許重復 數(shù)組 初始容量10, grow1.5倍
contains判斷元素存在
LinkedList ArrayList ArrayList
基于鏈表的數(shù)據(jù)結(jié)構(gòu)
remove只移除第一個; 迭代時remove拋ConcurrentModificationException(有特例,元素個數(shù)<=2); 有contains,get

ConcurrentHashMap key、value均不允許,put-null空指針異常; 同HashMap
HashMap+CAS無鎖算法
實際容量>=sizeCtl,則擴容 用foreach迭代,Map定義時必須制定key-value類型,否則cant convert containsKey、 containsValue


允許null:HashMap和以其為底層結(jié)構(gòu)的非同步集合;List ArrayList相關(guān)






有序: 先說明有序的概念:迭代輸出和存入順序一致即為有序(可以理解為先進先出FIFO)(注:Java8支持list逆序迭代,我們討論有序時忽略這個) 不要和TreeSet弄混了,TreeSet所謂的“有序”,指的是內(nèi)部存儲結(jié)構(gòu)有特定的存儲規(guī)則,它默認使用元素的自然順序?qū)υ剡M行排序,卻打亂了元素的存入順序。So,嚴格來講,TreeSet是無序的。 隨機訪問: 即使用[]操作符訪問其中的元素。

EnumMap:

Set keySet?=?enumMap.keySet();

Iterator iteKey?=?keySet.iterator();

while(iteKey.hasNext()){

????Object object?=(Object)?iteKey.next();

????System.out.print(object?+"="+?enumMap.get(object)+"; ");

}

Collection<Object>vals?=enumMap.values(); Set<Entry<Season,Object>>entrySet?=enumMap.entrySet();

HashMap:
  • 當某個桶中的鍵值對數(shù)量大于8個【9個起】,且桶數(shù)量大于等于64,則將底層實現(xiàn)從鏈表轉(zhuǎn)為紅黑樹 ;
  • int threshold; // 新的擴容resize臨界值,當實際大小(容量*填充比)大于臨界值時,會進行2倍擴容;
  • key是有可能是null的,并且會在0桶位位置;
  • tableSizeFor(int cap) { //計算下次需要調(diào)整大小的擴容resize臨界值;結(jié)果為>=cap的最小2的自然數(shù)冪(64-》64;65-》128)
  • ?length為2的整數(shù)冪保證了length - 1 最后一位(二進制表示)為1,從而保證了索引位置index即( hash &length-1)的最后一位同時有為0和為1的可能性,保證了散列的均勻性。length為2的冪保證了按位與最后一位的有效性,使哈希表散列更均勻。
  • resize】時【鏈表】的變化:?元素位置在【原位置】或【原位置+oldCap】
  • 鏈表轉(zhuǎn)紅黑樹后,【僅在擴容resize時】若樹變短,會恢復為鏈表。

LinkedHashMap:
  • remove后再put,集合結(jié)構(gòu)變化:只要未沖突,table不改變(想想put原理就好理解了);但鏈表改變,新元素始終在tail。
  • 顯式地指定為access order后【前提】,調(diào)用get()方法,導致對應的entry移動到雙向鏈表的最后位置(tail),但是table未沒變。
  • So LinkedHashMap元素有序存放,但并不保證其迭代順序一直不變
  • LinkedHashMap的每個bucket都存了這個bucket的before和after,且每個before(after)又存儲了自身的前驅(qū)后繼,直到null。
迭代: ? ? Iterator<Map.Entry>?iterl?=?map.entrySet().iterator(); ? ??利用ArrayList的【ListIterator】向前迭代: ListIterator<Map.Entry>?iterpre?=?new?ArrayList<Map.Entry>(map.entrySet()).listIterator(map.size()); while?(iterpre.hasPrevious())?{……}
ArrayList:
  • int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5倍(15 >> 1=7) 擴容是大約1.5倍擴容,HashMap則是剛好2倍擴容。
  • add(int index, E element);將當前處于該位置的元素(如果有的話)和所有后續(xù)元素向后移動(其索引加 1)【System.arraycopy】。
  • trimToSize()去掉預留元素的位置,返回一個新數(shù)組,新數(shù)組不含null,數(shù)組的size和elementData.length相等,以節(jié)省空間。此函數(shù)可避免size很小但elementData.length很大的情況。
  • 不建議使用contains(Object o)方法,看源碼就知道了,調(diào)用其內(nèi)置的indexOf方法,for循環(huán)一個個equals,這效率只能呵呵噠了,建議使用hashcode。
  • remove: 首先判斷要remove的元素是null還是非null,然后for循環(huán)查找,核心是fastRemove(index)方法。?fastRemove并不返回被移除的元素。??elementData[--size] = null;因為arraycopy方法是將elementData的index+1處開始的元素往前復制,也就是說最后一個數(shù)本該消除,但還在那里,所以需要置空。
  • subList方法得到的subList將和原來的list互相影響,不管你改哪一個,另一個都會隨之改變,而且當父list結(jié)構(gòu)改變時,子list會拋ConcurrentModificationException異常。解決方案:List<String> subListNew = new ArrayList(parentList.subList(1, 3));【類似Arrays.asList()方法】

ConcurrentHashMap:
  • CAS算法;unsafe.compareAndSwapInt(this, valueOffset, expect, update); ?CAS(Compare And Swap),意思是如果valueOffset位置包含的值與expect值相同,則更新valueOffset位置的值為update,并返回true,否則不更新,返回false。
  • 與Java8的HashMap有相通之處,底層依然由“數(shù)組”+鏈表+紅黑樹;
  • 底層結(jié)構(gòu)存放的是TreeBin對象,而不是TreeNode對象;
  • CAS作為知名無鎖算法,那ConcurrentHashMap就沒用鎖了么?當然不是,hash值相同的鏈表的頭結(jié)點還是會synchronized上鎖。?

private?transient?volatile?int?sizeCtl;

sizeCtl是控制標識符,不同的值表示不同的意義。

  • 負數(shù)代表正在進行初始化或擴容操作?
  • -1代表正在初始化?
  • -N 表示有N-1個線程正在進行擴容操作?
  • 正數(shù)或0代表hash表還沒有被初始化,這個數(shù)值表示初始化或下一次進行擴容的大小,類似于擴容閾值。它的值始終是當前ConcurrentHashMap容量的0.75倍,這與loadfactor是對應的。實際容量>=sizeCtl,則擴容。
concurrencyLevel ? ? 能夠同時更新ConccurentHashMap且不產(chǎn)生鎖競爭的最大線程數(shù),在Java8之前實際上就是ConcurrentHashMap中的分段鎖個數(shù),即Segment[]的數(shù)組長度正確地估計很重要,當?shù)凸?#xff0c;數(shù)據(jù)結(jié)構(gòu)將根據(jù)額外的競爭,從而導致線程試圖寫入當前鎖定的段時阻塞;相反,如果高估了并發(fā)級別,你遇到過大的膨脹,由于段的不必要的數(shù)量;?這種膨脹可能會導致性能下降,由于高數(shù)緩存未命中。 在Java8里,僅僅是為了兼容舊版本而保留。唯一的作用就是保證構(gòu)造map時初始容量不小于concurrencyLevel。 ForwardingNode:
? ? 并不是我們傳統(tǒng)的包含key-value的節(jié)點,只是一個標志節(jié)點,并且指向nextTable,提供find方法而已。生命周期:僅存活于擴容操作且bin不為null時,一定會出現(xiàn)在每個bin的首位。
3個原子操作(調(diào)用頻率很高)

static?final?<K,V> Node<K,V>?tabAt(Node<K,V>[]?tab,?int?i) {?// 獲取索引i處Node

????return?(Node<K,V>)U.getObjectVolatile(tab, ((long)i?<<?ASHIFT) +?ABASE);

????}

? ??// 利用CAS算法設(shè)置i位置上的Node節(jié)點(將c和table[i]比較,相同則插入v)。

????static?final?<K,V>?boolean?casTabAt(Node<K,V>[]?tab,?int?i,

????????????????????????????????????????Node<K,V>?c, Node<K,V>?v) {

????????return?U.compareAndSwapObject(tab, ((long)i?<<?ASHIFT) +?ABASE,?c,?v);

????}

? ??// 設(shè)置節(jié)點位置的值,僅在上鎖區(qū)被調(diào)用

????static?final?<K,V>?void?setTabAt(Node<K,V>[]?tab,?int?i, Node<K,V>?v) {

????????U.putObjectVolatile(tab, ((long)i?<<?ASHIFT) +?ABASE,?v);

????}

ConcurrentHashMap無鎖多線程擴容,減少擴容時的時間消耗。 transfer擴容操作:單線程構(gòu)建兩倍容量的nextTable;允許多線程復制原table元素到nextTable。
  • 為每個內(nèi)核均分任務,并保證其不小于16;
  • 若nextTab為null,則初始化其為原table的2倍;
  • 死循環(huán)遍歷,直到finishing。
    • 節(jié)點為空,則插入ForwardingNode;
    • 鏈表節(jié)點(fh>=0),分別插入nextTable的i和i+n的位置;【逆序鏈表??】
    • TreeBin節(jié)點(fh<0),判斷是否需要untreefi,分別插入nextTable的i和i+n的位置;【逆序樹??】
    • finishing時,nextTab賦給table,更新sizeCtl為新容量的0.75倍 ,完成擴容。

    以上說的都是單線程,多線程又是如何實現(xiàn)的呢? 遍歷到ForwardingNode節(jié)點((fh = f.hash) == MOVED),說明此節(jié)點被處理過了,直接跳過。這是控制并發(fā)擴容的核心?。由于給節(jié)點上了鎖,只允許當前線程完成此節(jié)點的操作,處理完畢后,將對應值設(shè)為ForwardingNode(fwd),其他線程看到forward,直接向后遍歷。如此便完成了多線程的復制工作,也解決了線程安全問題。
    2、 put相關(guān):
    理一下put的流程: ①判空:null直接拋空指針異常; ②hash:計算h=key.hashcode;調(diào)用spread計算hash=(h?^(h?>>>16))&?HASH_BITS; ③遍歷table
    • 若table為空,則初始化,僅設(shè)置相關(guān)參數(shù);
    • @@@計算當前key存放位置,即table的下標i=(n - 1) & hash;
    • 若待存放位置為null,casTabAt無鎖插入;
    • 若是forwarding nodes(檢測到正在擴容),則helpTransfer(幫助其擴容);
    • else(待插入位置非空且不是forward節(jié)點,即碰撞了),將頭節(jié)點上鎖(保證了線程安全):區(qū)分鏈表節(jié)點和樹節(jié)點,分別插入(遇到hash值與key值都與新節(jié)點一致的情況,只需要更新value值即可。否則依次向后遍歷,直到鏈表尾插入這個結(jié)點);
    • 若鏈表長度>8,則treeifyBin轉(zhuǎn)樹(Note:若length<64,直接tryPresize,兩倍table.length;不轉(zhuǎn)樹)。
    ④addCount(1L, binCount)。 Note: 1、put操作共計兩次hash操作,再利用“與&”操作計算Node的存放位置。 2、ConcurrentHashMap不允許key或value為null。 3、addCount(longx,?intcheck)方法: ①利用CAS快速更新baseCount的值; ②check>=0.則檢驗是否需要擴容;if?sizeCtl<0(正在進行初始化或擴容操作)【nexttable null等情況break;如果有線程正在擴容,則協(xié)助擴容】;else if?僅當前線程在擴容,調(diào)用協(xié)助擴容函數(shù),注其參數(shù)nextTable為null。

    ?以下為引用:?

    java提高篇(二十)-----集合大家族:http://demo.netfoucs.com/chenssy/article/details/17732841

    ????6.1、Vector和ArrayList

    ???????1,vector是線程同步的,所以它也是線程安全的,而arraylist是線程異步的,是不安全的。如果不考慮到線程的安全因素,一般用arraylist效率比較高。
    ???????2,如果集合中的元素的數(shù)目大于目前集合數(shù)組的長度時,vector增長率為目前數(shù)組長度的100%,而arraylist增長率為目前數(shù)組長度的50%.如過在集合中使用數(shù)據(jù)量比較大的數(shù)據(jù),用vector有一定的優(yōu)勢。
    ???????3,如果查找一個指定位置的數(shù)據(jù),vector和arraylist使用的時間是相同的,都是0(1),這個時候使用vector和arraylist都可以。而如果移動一個指定位置的數(shù)據(jù)花費的時間為0(n-i)n為總長度,這個時候就應該考慮到使用linklist,因為它移動一個指定位置的數(shù)據(jù)所花費的時間為0(1),而查詢一個指定位置的數(shù)據(jù)時花費的時間為0(i)。

    ???????ArrayList 和Vector是采用數(shù)組方式存儲數(shù)據(jù),此數(shù)組元素數(shù)大于實際存儲的數(shù)據(jù)以便增加和插入元素,都允許直接序號索引元素,但是插入數(shù)據(jù)要設(shè)計到數(shù)組元素移動等內(nèi)存操作,所以索引數(shù)據(jù)快插入數(shù)據(jù)慢,Vector由于使用了synchronized方法(線程安全)所以性能上比ArrayList要差,LinkedList使用雙向鏈表實現(xiàn)存儲,按序號索引數(shù)據(jù)需要進行向前或向后遍歷,但是插入數(shù)據(jù)時只需要記錄本項的前后項即可,所以插入數(shù)度較快!

    ??????6.2、Aarraylist和Linkedlist

    ???????1.ArrayList是實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu)。
    ???????2.對于隨機訪問get和set,ArrayList覺得優(yōu)于LinkedList,因為LinkedList要移動指針。
    ???????3.對于新增和刪除操作add和remove,LinedList比較占優(yōu)勢,因為ArrayList要移動數(shù)據(jù)。
    這一點要看實際情況的。若只對單條數(shù)據(jù)插入或刪除,ArrayList的速度反而優(yōu)于LinkedList。但若是批量隨機的插入刪除數(shù)據(jù),LinkedList的速度大大優(yōu)于ArrayList. 因為ArrayList每插入一條數(shù)據(jù),要移動插入點及之后的所有數(shù)據(jù)。

    ??????6.3、HashMap與TreeMap

    ???????1、HashMap通過hashcode對其內(nèi)容進行快速查找,而TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個有序的結(jié)果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。HashMap中元素的排列順序是不固定的)。

    ???????2、 HashMap通過hashcode對其內(nèi)容進行快速查找,而TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個有序的結(jié)果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。集合框架提供兩種常規(guī)的Map實現(xiàn):HashMap和TreeMap (TreeMap實現(xiàn)SortedMap接口)。

    ???????3、在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會更好。使用HashMap要求添加的鍵類明確定義了hashCode()和 equals()的實現(xiàn)。 這個TreeMap沒有調(diào)優(yōu)選項,因為該樹總處于平衡狀態(tài)。

    ??????6.4、hashtable與hashmap

    ???????1、歷史原因:Hashtable是基于陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現(xiàn) 。

    ???????2、同步性:Hashtable是線程安全的,也就是說是同步的,而HashMap是線程序不安全的,不是同步的 。

    ???????3、值:只有HashMap可以讓你將空值作為一個表的條目的key或value 。

    ?????? 七、對集合的選擇

    ??????7.1、對List的選擇

    ???????1、對于隨機查詢與迭代遍歷操作,數(shù)組比所有的容器都要快。所以在隨機訪問中一般使用ArrayList

    ???????2、LinkedList使用雙向鏈表對元素的增加和刪除提供了非常好的支持,而ArrayList執(zhí)行增加和刪除元素需要進行元素位移。

    ???????3、對于Vector而已,我們一般都是避免使用。

    ???????4、將ArrayList當做首選,畢竟對于集合元素而已我們都是進行遍歷,只有當程序的性能因為List的頻繁插入和刪除而降低時,再考慮LinkedList。

    ??????7.2、對Set的選擇

    ???????1、HashSet由于使用HashCode實現(xiàn),所以在某種程度上來說它的性能永遠比TreeSet要好,尤其是進行增加和查找操作。

    ???????3、雖然TreeSet沒有HashSet性能好,但是由于它可以維持元素的排序,所以它還是存在用武之地的。





    總結(jié)

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

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