摘要:集合中成員很豐富,常用的集合有,,等。實(shí)現(xiàn)接口的集合主要有。集合中不能包含重復(fù)的元素,每個(gè)元素必須是唯一的。而以作為實(shí)現(xiàn)的構(gòu)造函數(shù)的訪問(wèn)權(quán)限是默認(rèn)訪問(wèn)權(quán)限,即包內(nèi)訪問(wèn)權(quán)限。與接口不同,它是由一系列鍵值對(duì)組成的集合,提供了到的映射。
原文地址
Java集合Java集合框架:是一種工具類,就像是一個(gè)容器可以存儲(chǔ)任意數(shù)量的具有共同屬性的對(duì)象。
Java集合中成員很豐富,常用的集合有ArrayList,HashMap,HashSet等。線程安全的有Vector,HashTable。線程不安全的有LinkedList,TreeMap,ArrayList,HashMap等等。
集合中用到的數(shù)據(jù)結(jié)構(gòu)有以下幾種:
數(shù)組:最常用的數(shù)據(jù)結(jié)構(gòu)之一。數(shù)組的特點(diǎn)是長(zhǎng)度固定,可以用下標(biāo)索引,并且所有的元素的類型都是一致的。使用時(shí)盡量把數(shù)組封裝在一個(gè)類里,防止數(shù)據(jù)被錯(cuò)誤的操作弄亂。
鏈表:是一種由多個(gè)節(jié)點(diǎn)組成的數(shù)據(jù)結(jié)構(gòu),并且每個(gè)節(jié)點(diǎn)包含有數(shù)據(jù)以及指向下一個(gè)節(jié)點(diǎn)的引用,在雙向鏈表里,還會(huì)有一個(gè)指向前一個(gè)節(jié)點(diǎn)的引用。例如,可以用單向鏈表和雙向鏈表來(lái)實(shí)現(xiàn)堆棧和隊(duì)列,因?yàn)殒湵淼膬啥硕际强梢赃M(jìn)行插入和刪除的動(dòng)作的。當(dāng)然,也會(huì)有在鏈表的中間頻繁插入和刪除節(jié)點(diǎn)的場(chǎng)景。
樹(shù):是一種由節(jié)點(diǎn)組成的數(shù)據(jù)結(jié)構(gòu),每個(gè)節(jié)點(diǎn)都包含數(shù)據(jù)元素,并且有一個(gè)或多個(gè)子節(jié)點(diǎn),每個(gè)子節(jié)點(diǎn)指向一個(gè)父節(jié)點(diǎn)可以表示層級(jí)關(guān)系或者數(shù)據(jù)元素的順序關(guān)系。如果樹(shù)的每個(gè)子節(jié)點(diǎn)最多有兩個(gè)葉子節(jié)點(diǎn),那么這種樹(shù)被稱為二叉樹(shù)。二叉樹(shù)是一種非常常用的樹(shù)形結(jié)構(gòu), 因?yàn)樗倪@種結(jié)構(gòu)使得節(jié)點(diǎn)的插入和刪除都非常高效。樹(shù)的邊表示從一個(gè)節(jié)點(diǎn)到另外一個(gè)節(jié)點(diǎn)的快捷路徑。
堆棧:只允許對(duì)最后插入的元素進(jìn)行操作(也就是后進(jìn)先出,Last In First Out – LIFO)。如果你移除了棧頂?shù)脑兀敲茨憧梢圆僮鞯箶?shù)第二個(gè)元素,依次類推。這種后進(jìn)先出的方式是通過(guò)僅有的peek(),push()和pop()這幾個(gè)方法的強(qiáng)制性限制達(dá)到的。這種結(jié)構(gòu)在很多場(chǎng)景下都非常實(shí)用,例如解析像(4+2)*3這樣的數(shù)學(xué)表達(dá)式,把源碼中的方法和異常按照他們出現(xiàn)的順序放到堆棧中,檢查你的代碼看看小括號(hào)和花括號(hào)是不是匹配的,等等。
隊(duì)列:和堆棧有些相似,不同之處在于在隊(duì)列里第一個(gè)插入的元素也是第一個(gè)被刪除的元素(即是先進(jìn)先出)。這種先進(jìn)先出的結(jié)構(gòu)是通過(guò)只提供peek(),offer()和poll()這幾個(gè)方法來(lái)訪問(wèn)數(shù)據(jù)進(jìn)行限制來(lái)達(dá)到的。例如,排隊(duì)等待公交車,銀行或者超市里的等待列隊(duì)等等,都是可以用隊(duì)列來(lái)表示。
Java集合框架圖
[圖片上傳失敗...(image-4b8b54-1530872801038)]
Collection interface如上圖所示,Collection接口是最基本的集合接口,它不提供直接的實(shí)現(xiàn),Java SDK提供的類都是繼承自Collection的“子接口”如List,Set和Queue。Collection所代表的是一種規(guī)則,它所包含的元素都必須遵循一條或者多條規(guī)則。如有些允許出現(xiàn)重復(fù)元素而有些則不允許重復(fù)、有些必須要按照順序插入而有些則是散列,有些支持排序但是有些則不支持等等。
ListList接口是Collection接口下的子接口。List所代表的是有序的Collection,即它用某種特定的插入順序來(lái)維護(hù)元素順序。用戶可以對(duì)列表中每個(gè)元素的插入位置進(jìn)行精確地控制,同時(shí)可以根據(jù)元素的整數(shù)索引(在列表中的位置)訪問(wèn)元素,并搜索列表中的元素。實(shí)現(xiàn)List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
ArrayListArrayList基于數(shù)組實(shí)現(xiàn),可以通過(guò)下標(biāo)索引直接查找到指定位置的元素,因此查找效率高,但每次插入或刪除元素,就要大量地移動(dòng)元素,插入刪除元素的效率低。它允許任何符合規(guī)則的元素插入甚至包括null。每一個(gè)ArrayList都有一個(gè)初始容量(10),該容量代表了數(shù)組的大小。隨著容器中的元素不斷增加,容器的大小也會(huì)隨著增加。在每次向容器中增加元素的同時(shí)都會(huì)進(jìn)行容量檢查,當(dāng)快溢出時(shí),就會(huì)進(jìn)行擴(kuò)容操作(擴(kuò)容1.5倍)。所以如果我們明確所插入元素的多少,最好指定一個(gè)初始容量值,避免過(guò)多的進(jìn)行擴(kuò)容操作而浪費(fèi)時(shí)間、效率。
ArrayList擅長(zhǎng)于隨機(jī)訪問(wèn)。同時(shí)ArrayList是非同步的,只能用在單線程環(huán)境下,多線程環(huán)境下可以考慮用Collections.synchronizedList(List l)函數(shù)返回一個(gè)線程安全的ArrayList類,也可以使用concurrent并發(fā)包下的CopyOnWriteArrayList類。
擴(kuò)充容量的方法ensureCapacity。ArrayList在每次增加元素(可能是1個(gè),也可能是一組)時(shí),都要調(diào)用該方法來(lái)確保足夠的容量。當(dāng)容量不足以容納當(dāng)前的元素個(gè)數(shù)時(shí),就設(shè)置新的容量為舊的容量的1.5倍,如果設(shè)置后的新容量還不夠,則直接新容量設(shè)置為傳入的參數(shù)(也就是所需的容量),而后用Arrays.copyof()方法將元素拷貝到新的數(shù)組。從中可以看出,當(dāng)容量不夠時(shí),每次增加元素,都要將原來(lái)的元素拷貝到一個(gè)新的數(shù)組中,非常之耗時(shí),也因此建議在事先能確定元素?cái)?shù)量的情況下,才使用ArrayList,否則建議使用LinkedList。
ArrayList的具體實(shí)現(xiàn)請(qǐng)參考這里
LinkedListLinkedList同樣實(shí)現(xiàn)List接口,與ArrayList不同的是,LinkedList是基于雙向鏈表實(shí)現(xiàn)的,可以在任何位置進(jìn)行高效地插入和移除操作。但是LinkedList不能隨機(jī)訪問(wèn),它所有的操作都是要按照雙重鏈表的需要執(zhí)行。在列表中索引的操作將從開(kāi)頭或結(jié)尾遍歷列表(從靠近指定索引的一端)。這樣做的好處就是可以通過(guò)較低的代價(jià)在List中進(jìn)行插入和刪除操作。
與ArrayList一樣,LinkedList也是非同步的。如果多個(gè)線程同時(shí)訪問(wèn)一個(gè)List,則必須自己實(shí)現(xiàn)訪問(wèn)同步。一種解決方法是在創(chuàng)建List時(shí)構(gòu)造一個(gè)同步的List:
List list = Collections.synchronizedList(new LinkedList(…));
LinkedList的具體實(shí)現(xiàn)請(qǐng)參考這里
Vector與ArrayList相似,但是Vector是同步的。所以說(shuō)Vector是線程安全的動(dòng)態(tài)數(shù)組。它的操作與ArrayList幾乎一樣。
Vector的具體實(shí)現(xiàn)請(qǐng)參考這里
StackStack繼承自Vector,實(shí)現(xiàn)一個(gè)后進(jìn)先出的堆棧。Stack提供5個(gè)額外的方法使得Vector得以被當(dāng)作堆棧使用。基本的push和pop 方法,還有peek方法得到棧頂?shù)脑兀琫mpty方法測(cè)試堆棧是否為空,search方法檢測(cè)一個(gè)元素在堆棧中的位置。Stack剛創(chuàng)建后是空棧。
Stack的具體實(shí)現(xiàn)請(qǐng)參考這里
SetSet接口繼承了Collection接口。Set集合中不能包含重復(fù)的元素,每個(gè)元素必須是唯一的。你只需將元素加入set中,重復(fù)的元素會(huì)自動(dòng)移除。有三種常見(jiàn)的Set實(shí)現(xiàn)——HashSet, TreeSet和LinkedHashSet。如果你需要一個(gè)訪問(wèn)快速的Set,你應(yīng)該使用HashSet;當(dāng)你需要一個(gè)排序的Set,你應(yīng)該使用TreeSet;當(dāng)你需要記錄下插入時(shí)的順序時(shí),你應(yīng)該使用LinedHashSet。
HashSetHashSet是是基于 HashMap 實(shí)現(xiàn)的,底層采用 HashMap 來(lái)保存元素,所以它不保證set 的迭代順序;特別是它不保證該順序恒久不變。add()、remove()以及contains()等方法都是復(fù)雜度為O(1)的方法。由于HashMap中key不可重復(fù),所以HashSet元素不可重復(fù)。可以存儲(chǔ)null元素,是線程不安全的。
TreeSetTreeSet是一個(gè)有序集,基于TreeMap實(shí)現(xiàn),是線程不安全的。
TreeSet底層采用TreeMap存儲(chǔ),構(gòu)造器啟動(dòng)時(shí)新建TreeMap。TreeSet存儲(chǔ)元素實(shí)際為TreeMap存儲(chǔ)的鍵值對(duì)為
TreeSet支持兩種兩種排序方式,通過(guò)不同構(gòu)造器調(diào)用實(shí)現(xiàn)
自然排序:
public TreeSet() { // 新建TreeMap,自然排序 this(new TreeMap()); }
Comparator排序:
public TreeSet(Comparator super E> comparator) { // 新建TreeMap,傳入自定義比較器comparator this(new TreeMap<>(comparator)); }
TreeSet支持正向/反向迭代器遍歷和foreach遍歷
// 順序TreeSet:迭代器實(shí)現(xiàn) Iterator iter = set.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } // 順序遍歷TreeSet:foreach實(shí)現(xiàn) for (Integer i : set) { System.out.println(i); } // 逆序遍歷TreeSet:反向迭代器實(shí)現(xiàn) Iterator iter1 = set.descendingIterator(); while (iter1.hasNext()) { System.out.println(iter1.next()); }LinkedHashSet
LinkedHashSet介于HashSet和TreeSet之間。哈希表和鏈接列表實(shí)現(xiàn)。基本方法的復(fù)雜度為O(1)。
LinkedHashSet 是 Set 的一個(gè)具體實(shí)現(xiàn),其維護(hù)著一個(gè)運(yùn)行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序可為插入順序或是訪問(wèn)順序。
LinkedHashSet 繼承于 HashSet,并且其內(nèi)部是通過(guò) LinkedHashMap 來(lái)實(shí)現(xiàn)的。有點(diǎn)類似于我們之前說(shuō)的LinkedHashMap 其內(nèi)部是基于 Hashmap 實(shí)現(xiàn)的一樣。
如果我們需要迭代的順序?yàn)椴迦腠樞蚧蛘咴L問(wèn)順序,那么 LinkedHashSet 是需要你首先考慮的。
LinkedHashSet 底層使用 LinkedHashMap 來(lái)保存所有元素,因?yàn)槔^承于 HashSet,所有的方法操作上又與 HashSet 相同,因此 LinkedHashSet 的實(shí)現(xiàn)上非常簡(jiǎn)單,只提供了四個(gè)構(gòu)造方法,并通過(guò)傳遞一個(gè)標(biāo)識(shí)參數(shù),調(diào)用父類的構(gòu)造器,底層構(gòu)造一個(gè) LinkedHashMap 來(lái)實(shí)現(xiàn),在相關(guān)操作上與父類 HashSet 的操作相同,直接調(diào)用父類 HashSet 的方法即可。
package java.util; public class LinkedHashSetextends HashSet implements Set , Cloneable, java.io.Serializable { private static final long serialVersionUID = -2851667679971038690L; /** * 構(gòu)造一個(gè)帶有指定初始容量和加載因子的空鏈表哈希set。 * * 底層會(huì)調(diào)用父類的構(gòu)造方法,構(gòu)造一個(gè)有指定初始容量和加載因子的LinkedHashMap實(shí)例。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 */ public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } /** * 構(gòu)造一個(gè)指定初始容量和默認(rèn)加載因子0.75的新鏈表哈希set。 * * 底層會(huì)調(diào)用父類的構(gòu)造方法,構(gòu)造一個(gè)指定初始容量和默認(rèn)加載因子0.75的LinkedHashMap實(shí)例。 * @param initialCapacity 初始容量。 */ public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } /** * 構(gòu)造一個(gè)默認(rèn)初始容量16和加載因子0.75的新鏈表哈希set。 * * 底層會(huì)調(diào)用父類的構(gòu)造方法,構(gòu)造一個(gè)默認(rèn)初始容量16和加載因子0.75的LinkedHashMap實(shí)例。 */ public LinkedHashSet() { super(16, .75f, true); } /** * 構(gòu)造一個(gè)與指定collection中的元素相同的新鏈表哈希set。 * * 底層會(huì)調(diào)用父類的構(gòu)造方法,構(gòu)造一個(gè)足以包含指定collection * 中所有元素的初始容量和加載因子為0.75的LinkedHashMap實(shí)例。 * @param c 其中的元素將存放在此set中的collection。 */ public LinkedHashSet(Collection extends E> c) { super(Math.max(2*c.size(), 11), .75f, true); addAll(c); } @Override public Spliterator spliterator() { return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED); } }
通過(guò)觀察HashMap的源碼我們可以發(fā)現(xiàn):
Hash Map的前三個(gè)構(gòu)造函數(shù),即訪問(wèn)權(quán)限為public類型的構(gòu)造函數(shù)均是以HashMap作為實(shí)現(xiàn)。而以LinkedHashMap作為實(shí)現(xiàn)的構(gòu)造函數(shù)的訪問(wèn)權(quán)限是默認(rèn)訪問(wèn)權(quán)限,即包內(nèi)訪問(wèn)權(quán)限。
即:在java編程中,通過(guò)new創(chuàng)建的HashSet對(duì)象均是以HashMap作為實(shí)現(xiàn)基礎(chǔ)。只有在jdk中java.util包內(nèi)的源代碼才可能創(chuàng)建以LinkedHashMap作為實(shí)現(xiàn)的HashSet(LinkedHashSet就是通過(guò)封裝一個(gè)以LinkedHashMap為實(shí)現(xiàn)的HashSet來(lái)實(shí)現(xiàn)的)。
只有包含三個(gè)參數(shù)的構(gòu)造函數(shù)才是采用的LinkedHashMap作為實(shí)現(xiàn)。
MapMap與List、Set接口不同,它是由一系列鍵值對(duì)組成的集合,提供了key到Value的映射。同時(shí)它也沒(méi)有繼承Collection。在Map中它保證了key與value之間的一一對(duì)應(yīng)關(guān)系。也就是說(shuō)一個(gè)key對(duì)應(yīng)一個(gè)value,所以它不能存在相同的key值,當(dāng)然value值可以相同。key可以為空,但是只允許出現(xiàn)一個(gè)null。它的主要實(shí)現(xiàn)類有HashMap、HashTable、LinkedHashMap、TreeMap。
HashMapHashMap 是 Map 的一個(gè)實(shí)現(xiàn)類,它代表的是一種鍵值對(duì)的數(shù)據(jù)存儲(chǔ)形式。
大多數(shù)情況下可以直接定位到它的值,因而具有很快的訪問(wèn)速度,但遍歷順序卻是不確定的。
HashMap最多只允許一條記錄的鍵為null,允許多條記錄的值為null。遇到key為null的時(shí)候,調(diào)用putForNullKey方法進(jìn)行處理,而對(duì)value沒(méi)有處理。不保證有序(比如插入的順序)、也不保證序不隨時(shí)間變化。
jdk 8 之前,其內(nèi)部是由數(shù)組+鏈表來(lái)實(shí)現(xiàn)的,而 jdk 8 對(duì)于鏈表長(zhǎng)度超過(guò) 8 的鏈表將轉(zhuǎn)儲(chǔ)為紅黑樹(shù)。
HashMap非線程安全,即任一時(shí)刻可以有多個(gè)線程同時(shí)寫(xiě)HashMap,可能會(huì)導(dǎo)致數(shù)據(jù)的不一致。如果需要滿足線程安全,可以用 Collections的synchronizedMap方法使HashMap具有線程安全的能力,或者使用ConcurrentHashMap。
hash數(shù)組的默認(rèn)大小是16,而且大小一定是2的指數(shù)
HashMap的具體實(shí)現(xiàn)請(qǐng)參考這里
HashTableHashtable和HashMap一樣也是散列表,存儲(chǔ)元素也是鍵值對(duì),底層實(shí)現(xiàn)是一個(gè)Entry數(shù)組+鏈表。Hashtable繼承于Dictionary類(Dictionary類聲明了操作鍵值對(duì)的接口方法),實(shí)現(xiàn)Map接口(定義鍵值對(duì)接口)。HashTable是線程安全的,它的大部分類都被synchronized關(guān)鍵字修飾。key和value都不可為null。
hash數(shù)組默認(rèn)大小是11,擴(kuò)充方式是old*2+1
LinkedHashMapLinkedHashMap繼承自HashMap實(shí)現(xiàn)了Map接口。基本實(shí)現(xiàn)同HashMap一樣(底層基于數(shù)組+鏈表+紅黑樹(shù)實(shí)現(xiàn)),不同之處在于LinkedHashMap保證了迭代的有序性。其內(nèi)部維護(hù)了一個(gè)雙向鏈表,解決了 HashMap不能隨時(shí)保持遍歷順序和插入順序一致的問(wèn)題。
除此之外,LinkedHashMap對(duì)訪問(wèn)順序也提供了相關(guān)支持。在一些場(chǎng)景下,該特性很有用,比如緩存。
在實(shí)現(xiàn)上,LinkedHashMap很多方法直接繼承自HashMap,僅為維護(hù)雙向鏈表覆寫(xiě)了部分方法。
默認(rèn)情況下,LinkedHashMap的迭代順序是按照插入節(jié)點(diǎn)的順序。也可以通過(guò)改變accessOrder參數(shù)的值,使得其遍歷順序按照訪問(wèn)順序輸出。
LinkedHashMap的具體實(shí)現(xiàn)請(qǐng)參考這里
TreeMapTreeMap繼承自AbstractMap抽象類,并實(shí)現(xiàn)了SortedMap接口,如下圖所示:
[圖片上傳失敗...(image-fd7a40-1530872801038)]
TreeMap集合是基于紅黑樹(shù)(Red-Black tree)的 NavigableMap實(shí)現(xiàn)。該集合最重要的特點(diǎn)就是可排序,該映射根據(jù)其鍵的自然順序進(jìn)行排序,或者根據(jù)創(chuàng)建映射時(shí)提供的 Comparator 進(jìn)行排序,具體取決于使用的構(gòu)造方法。
關(guān)于集合的常見(jiàn)問(wèn)題 List和Map的區(qū)別都是Java常用的容器,都是接口。不同的是List存儲(chǔ)的是單列的集合,Map存儲(chǔ)的是key-value鍵值對(duì)的集合。List中允許出現(xiàn)重復(fù)元素,Map中不允許key重復(fù)。List集合是有序的(儲(chǔ)存有序),Map集合是無(wú)序的(存儲(chǔ)無(wú)序)
Set中的元素不能重復(fù),如何實(shí)現(xiàn)?Set大多都用的Map接口的實(shí)現(xiàn)類來(lái)實(shí)現(xiàn)的(HashSet基于HashMap實(shí)現(xiàn),TreeSet基于TreeMap實(shí)現(xiàn),LinkedHashSet基于LinkedHashMap實(shí)現(xiàn))
在HashMap中通過(guò)如下實(shí)現(xiàn)來(lái)保證key值唯一
// 1. 如果key 相等 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 2. 修改對(duì)應(yīng)的value if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; }
添加元素的時(shí)候,如果key(也對(duì)應(yīng)的Set集合的元素)相等,那么則修改value值。而在Set集合中,value值僅僅是一個(gè)Object對(duì)象罷了(該對(duì)象對(duì)Set本身而言是無(wú)用的)。
也就是說(shuō):Set集合如果添加的元素相同時(shí),是根本沒(méi)有插入的(僅修改了一個(gè)無(wú)用的value值)。從源碼(HashMap)中也看出來(lái),==和equals()方法都有使用!
Vector和ArrayList相同點(diǎn):
這兩個(gè)類都實(shí)現(xiàn)了List接口,他們都是有序的集合(儲(chǔ)存有序),底層都用數(shù)組實(shí)現(xiàn)。可以通過(guò)索引來(lái)獲取某個(gè)元素。允許元素重復(fù)和出現(xiàn)null值。ArrayList和Vector的迭代器實(shí)現(xiàn)都是fail-fast的。
不同點(diǎn):
vector是線程同步的,所以它也是線程安全的,而arraylist是線程異步的,是不安全的。如果不考慮到線程的安全因素,一般用arraylist效率比較高。
擴(kuò)容時(shí),arraylist擴(kuò)容1.5倍,vector擴(kuò)容2倍(或者擴(kuò)容指定的大小)
ArrayList 和Vector是采用數(shù)組方式存儲(chǔ)數(shù)據(jù),此數(shù)組元素?cái)?shù)大于實(shí)際存儲(chǔ)的數(shù)據(jù)以便增加和插入元素,都允許直接序號(hào)索引元素,但是插入數(shù)據(jù)要設(shè)計(jì)到數(shù)組元素移動(dòng)等內(nèi)存操作,所以索引數(shù)據(jù)快插入數(shù)據(jù)慢,Vector由于使用了synchronized方法(線程安全)所以性能上比ArrayList要差,LinkedList使用雙向鏈表實(shí)現(xiàn)存儲(chǔ),按序號(hào)索引數(shù)據(jù)需要進(jìn)行向前或向后遍歷,但是插入數(shù)據(jù)時(shí)只需要記錄本項(xiàng)的前后項(xiàng)即可,所以插入數(shù)度較快!
Aarraylist和LinkedlistArrayList是基于數(shù)組實(shí)現(xiàn)的,LinkedList基于雙向鏈表實(shí)現(xiàn)的。
ArrayList它支持以下標(biāo)位置進(jìn)行索引出對(duì)應(yīng)的元素(隨機(jī)訪問(wèn)),而LinkedList則需要遍歷整個(gè)鏈表來(lái)獲取對(duì)應(yīng)的元素。因此一般來(lái)說(shuō)ArrayList的訪問(wèn)速度是要比LinkedList要快的
ArrayList由于是數(shù)組,對(duì)于刪除和修改而言消耗是比較大(復(fù)制和移動(dòng)數(shù)組實(shí)現(xiàn)),LinkedList是雙向鏈表刪除和修改只需要修改對(duì)應(yīng)的指針即可,消耗是很小的。因此一般來(lái)說(shuō)LinkedList的增刪速度是要比ArrayList要快的
LinkedList比ArrayList消耗更多的內(nèi)存,因?yàn)長(zhǎng)inkedList中的每個(gè)節(jié)點(diǎn)存儲(chǔ)了前后節(jié)點(diǎn)的引用。
對(duì)于增加/刪除元素操作
如果增刪都是在末尾來(lái)操作(每次調(diào)用的都是remove()和add()),此時(shí)ArrayList就不需要移動(dòng)和復(fù)制數(shù)組來(lái)進(jìn)行操作了。如果數(shù)據(jù)量有百萬(wàn)級(jí)的時(shí),速度是會(huì)比LinkedList要快的。
如果刪除操作的位置是在中間。由于LinkedList的消耗主要是在遍歷上,ArrayList的消耗主要是在移動(dòng)和復(fù)制上(底層調(diào)用的是arraycopy()方法,是native方法)。
LinkedList的遍歷速度是要慢于ArrayList的復(fù)制移動(dòng)速度的
如果數(shù)據(jù)量有百萬(wàn)級(jí)的時(shí),還是ArrayList要快。
ArrayList、HashMap、TreeMap和HashTable類提供對(duì)元素的隨機(jī)訪問(wèn)。
Enumeration和Iterator接口的區(qū)別Enumeration的速度是Iterator的兩倍,也使用更少的內(nèi)存。Enumeration是非常基礎(chǔ)的,也滿足了基礎(chǔ)的需要。但是,與Enumeration相比,Iterator更加安全,因?yàn)楫?dāng)一個(gè)集合正在被遍歷的時(shí)候,它會(huì)阻止其它線程去修改集合。
Iterator的方法名比Enumeration更科學(xué)
Iterator有fail-fast機(jī)制,比Enumeration更安全
Iterator能夠刪除元素,Enumeration并不能刪除元素
我們可以使用Iterator來(lái)遍歷Set和List集合,而ListIterator只能遍歷List。
Iterator只可以向前遍歷,而LIstIterator可以雙向遍歷。
ListIterator從Iterator接口繼承,然后添加了一些額外的功能,比如添加一個(gè)元素、替換一個(gè)元素、獲取前面或后面元素的索引位置。
需要同時(shí)重寫(xiě)該類的hashCode()方法和它的equals()方法。
從源碼可以得知,在插入元素的時(shí)候是先算出該對(duì)象的hashCode。如果hashcode相等話的。那么表明該對(duì)象是存儲(chǔ)在同一個(gè)位置上的。
如果調(diào)用equals()方法,兩個(gè)key相同,則替換元素
如果調(diào)用equals()方法,兩個(gè)key不相同,則說(shuō)明該hashCode僅僅是碰巧相同,此時(shí)是散列沖突,將新增的元素放在桶子上
重寫(xiě)了equals()方法,就要重寫(xiě)hashCode()的方法。因?yàn)閑quals()認(rèn)定了這兩個(gè)對(duì)象相同,而同一個(gè)對(duì)象調(diào)用hashCode()方法時(shí),是應(yīng)該返回相同的值的!
HashSet與HashMapHashSet 實(shí)現(xiàn)了 Set 接口,它不允許集合中有重復(fù)的值,當(dāng)我們提到 HashSet 時(shí),第一件事情就是在將對(duì)象存儲(chǔ)在 HashSet 之前,要先確保對(duì)象重寫(xiě) equals()和 hashCode()方法,這樣才能比較對(duì)象的值是否相等,以確保set中沒(méi)有儲(chǔ)存相等的對(duì)象。如果我們沒(méi)有重寫(xiě)這兩個(gè)方法,將會(huì)使用這個(gè)方法的默認(rèn)實(shí)現(xiàn)。
public boolean add(Object o)方法用來(lái)在 Set 中添加元素,當(dāng)元素值重復(fù)時(shí)則會(huì)立即返回 false,如果成功添加的話會(huì)返回 true。
HashMap 實(shí)現(xiàn)了 Map 接口,Map 接口對(duì)鍵值對(duì)進(jìn)行映射。Map 中不允許重復(fù)的鍵。Map 接口有兩個(gè)基本的實(shí)現(xiàn),HashMap 和 TreeMap。TreeMap 保存了對(duì)象的排列次序,而 HashMap 則不能。HashMap 允許鍵和值為 null。HashMap 是非 synchronized 的,但 collection 框架提供方法能保證 HashMap synchronized,這樣多個(gè)線程同時(shí)訪問(wèn) HashMap 時(shí),能保證只有一個(gè)線程更改 Map。
public Object put(Object Key,Object value)方法用來(lái)將元素添加到 map 中。
HashMap | HashSet |
---|---|
HashMap實(shí)現(xiàn)了Map接口 | HashSet實(shí)現(xiàn)了Set接口 |
HashMap儲(chǔ)存鍵值對(duì) | HashSet僅僅存儲(chǔ)對(duì)象 |
使用put()方法將元素放入map中 | 使用add()方法將元素放入set中 |
HashMap中使用鍵對(duì)象來(lái)計(jì)算hashcode值 | HashSet使用成員對(duì)象來(lái)計(jì)算hashcode值,對(duì)于兩個(gè)對(duì)象來(lái)說(shuō)hashcode可能相同,所以equals()方法用來(lái)判斷對(duì)象的相等性,如果兩個(gè)對(duì)象不同的話,那么返回false |
相同點(diǎn):
儲(chǔ)存結(jié)構(gòu)和實(shí)現(xiàn)基本相同,都是是實(shí)現(xiàn)的Map接口
不同點(diǎn):
HashTable是同步的,HashMap是非同步的,需要同步的時(shí)候可以ConcurrentHashMap方法
HashMap允許為null,HashTable不允許為null
繼承不同,HashMap繼承的是AbstractMap,HashTable繼承的是Dictionary
HashMap提供對(duì)key的Set進(jìn)行遍歷,因此它是fail-fast的,但HashTable提供對(duì)key的Enumeration進(jìn)行遍歷,它不支持fail-fast。
HashTable是一個(gè)遺留類,如果需要保證線程安全推薦使用CocurrentHashMap
HashMap與TreeMapHashMap通過(guò)hashcode對(duì)其內(nèi)容進(jìn)行快速查找,而TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個(gè)有序的結(jié)果你就應(yīng)該使用TreeMap(HashMap中元素的排列順序是不固定的)。HashMap中元素的排列順序是不固定的)。
在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會(huì)更好。使用HashMap要求添加的鍵類明確定義了hashCode()和 equals()的實(shí)現(xiàn)。 這個(gè)TreeMap沒(méi)有調(diào)優(yōu)選項(xiàng),因?yàn)樵摌?shù)總處于平衡狀態(tài)。
集合框架中的泛型有什么優(yōu)點(diǎn)?Java1.5引入了泛型,所有的集合接口和實(shí)現(xiàn)都大量地使用它。泛型允許我們?yōu)榧咸峁┮粋€(gè)可以容納的對(duì)象類型,因此,如果你添加其它類型的任何元素,它會(huì)在編譯時(shí)報(bào)錯(cuò)。這避免了在運(yùn)行時(shí)出現(xiàn)ClassCastException,因?yàn)槟銓?huì)在編譯時(shí)得到報(bào)錯(cuò)信息。泛型也使得代碼整潔,我們不需要使用顯式轉(zhuǎn)換和instanceOf操作符。它也給運(yùn)行時(shí)帶來(lái)好處,因?yàn)椴粫?huì)產(chǎn)生類型檢查的字節(jié)碼指令。
comparable 和 comparator的不同之處?comparable接口實(shí)際上是出自java.lang包
它有一個(gè) compareTo(Object obj)方法來(lái)將objects排序
comparator接口實(shí)際上是出自 java.util 包
它有一個(gè)compare(Object obj1, Object obj2)方法來(lái)將objects排序
Vector, Hashtable, Properties 和 Stack 都是同步的類,所以它們都線程安全的,可以被使用在多線程環(huán)境中
使用Collections.synchronizedList(list)) 方法,可以保證list類是線程安全的
使用java.util.Collections.synchronizedSet()方法可以保證set類是線程安全的。
TreeSet要求存放的對(duì)象所屬的類必須實(shí)現(xiàn)Comparable接口,該接口提供了比較元素的compareTo()方法,當(dāng)插入元素時(shí)會(huì)回調(diào)該方法比較元素的大小。
TreeMap要求存放的鍵值對(duì)映射的鍵必須實(shí)現(xiàn)Comparable接口從而根據(jù)鍵對(duì)元素進(jìn)行排序。
Collections工具類的sort方法有兩種重載的形式,第一種要求傳入的待排序容器中存放的對(duì)象比較實(shí)現(xiàn)Comparable接口以實(shí)現(xiàn)元素的比較;第二種不強(qiáng)制性的要求容器中的元素必須可比較,但是要求傳入第二個(gè)參數(shù),參數(shù)是Comparator接口的子類型(需要重寫(xiě)compare方法實(shí)現(xiàn)元素的比較),相當(dāng)于一個(gè)臨時(shí)定義的排序規(guī)則,其實(shí)就是通過(guò)接口注入比較元素大小的算法,也是對(duì)回調(diào)模式的應(yīng)用(Java中對(duì)函數(shù)式編程的支持)。
什么是Java優(yōu)先級(jí)隊(duì)列?Java PriorityQueue是一個(gè)數(shù)據(jù)結(jié)構(gòu),它是Java集合框架的一部分。 它是一個(gè)隊(duì)列的實(shí)現(xiàn),其中元素的順序?qū)⒏鶕?jù)每個(gè)元素的優(yōu)先級(jí)來(lái)決定。 實(shí)例化PriorityQueue時(shí),可以在構(gòu)造函數(shù)中提供比較器。 該比較器將決定PriorityQueue集合實(shí)例中元素的排序順序。
Java hashCode()和equals()方法。equals()方法用于確定兩個(gè)Java對(duì)象的相等性。 當(dāng)我們有一個(gè)自定義類時(shí),我們需要重寫(xiě)equals()方法并提供一個(gè)實(shí)現(xiàn),以便它可以用來(lái)找到它的兩個(gè)實(shí)例之間的相等性。 通過(guò)Java規(guī)范,equals()和hashCode()之間有一個(gè)契約。 它說(shuō),“如果兩個(gè)對(duì)象相等,即obj1.equals(obj2)為true,那么obj1.hashCode()和obj2.hashCode()必須返回相同的整數(shù)”
無(wú)論何時(shí)我們選擇重寫(xiě)equals(),我們都必須重寫(xiě)hashCode()方法。 hashCode()用于計(jì)算位置存儲(chǔ)區(qū)和key。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/71493.html
摘要:下面是一些常見(jiàn)的理解性問(wèn)題,每一個(gè)問(wèn)題盡量用圖或代碼去描述。內(nèi)容全部來(lái)自,包括基本語(yǔ)法數(shù)組集合類泛型面向?qū)ο罄厥债惓?刂戚斎胼敵龊蛢?nèi)存。不斷更新,歡迎大家提出有趣味的問(wèn)題和意見(jiàn)。 程序員經(jīng)常可以通過(guò)搜索或者記憶來(lái)完成代碼,但是許多時(shí)候并不真正理解為什么那樣寫(xiě)。也就是說(shuō),有一定經(jīng)驗(yàn)的程序員不會(huì)犯一些低級(jí)的語(yǔ)法錯(cuò)誤,但是因?yàn)椴簧钊肜斫庥锌赡茉斐梢恍└呒?jí)錯(cuò)誤,比如說(shuō)運(yùn)行無(wú)效率,代碼難De...
摘要:編程思想第版這本書(shū)要常讀,初學(xué)者可以快速概覽,中等程序員可以深入看看,老鳥(niǎo)還可以用之回顧的體系。以下視頻整理自慕課網(wǎng)工程師路徑相關(guān)免費(fèi)課程。 我自己總結(jié)的Java學(xué)習(xí)的系統(tǒng)知識(shí)點(diǎn)以及面試問(wèn)題,目前已經(jīng)開(kāi)源,會(huì)一直完善下去,歡迎建議和指導(dǎo)歡迎Star: https://github.com/Snailclimb/Java-Guide 筆者建議初學(xué)者學(xué)習(xí)Java的方式:看書(shū)+視頻+實(shí)踐(初...
摘要:動(dòng)畫(huà)占用大量?jī)?nèi)存,如何優(yōu)化使用動(dòng)畫(huà)的注意事項(xiàng)有哪些問(wèn)題這個(gè)問(wèn)題主要出現(xiàn)在幀動(dòng)畫(huà)中,當(dāng)圖片數(shù)量較多且圖片較大時(shí)就極易出現(xiàn),這個(gè)在實(shí)際開(kāi)發(fā)中要尤其注意,盡量避免使用幀動(dòng)畫(huà)。 目錄介紹 4.0.0.1 Android中有哪幾種類型的動(dòng)畫(huà),屬性動(dòng)畫(huà)和補(bǔ)間動(dòng)畫(huà)有何區(qū)別?補(bǔ)間動(dòng)畫(huà)和屬性動(dòng)畫(huà)常用的有哪些? 4.0.0.2 View動(dòng)畫(huà)為何不能真正改變View的位置?而屬性動(dòng)畫(huà)為何可以?屬性動(dòng)畫(huà)是如...
摘要:中的詳解必修個(gè)多線程問(wèn)題總結(jié)個(gè)多線程問(wèn)題總結(jié)有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升開(kāi)源的運(yùn)行原理從虛擬機(jī)工作流程看運(yùn)行原理。 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 基于 POI 封裝 ExcelUtil 精簡(jiǎn)的 Excel 導(dǎo)入導(dǎo)出 由于 poi 本身只是針對(duì)于 ...
摘要:目錄介紹問(wèn)題匯總具體問(wèn)題好消息博客筆記大匯總年月到至今,包括基礎(chǔ)及深入知識(shí)點(diǎn),技術(shù)博客,學(xué)習(xí)筆記等等,還包括平時(shí)開(kāi)發(fā)中遇到的匯總,當(dāng)然也在工作之余收集了大量的面試題,長(zhǎng)期更新維護(hù)并且修正,持續(xù)完善開(kāi)源的文件是格式的同時(shí)也開(kāi)源了生活博客,從年 目錄介紹 00.Java問(wèn)題匯總 01.具體問(wèn)題 好消息 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識(shí)點(diǎn),Android技...
閱讀 1027·2022-07-19 10:19
閱讀 1803·2021-09-02 15:15
閱讀 1016·2019-08-30 15:53
閱讀 2660·2019-08-30 13:45
閱讀 2659·2019-08-26 13:57
閱讀 1991·2019-08-26 12:13
閱讀 1013·2019-08-26 10:55
閱讀 553·2019-08-26 10:46