日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Java集合框架體系總覽

本文轉(zhuǎn)載自微信公眾號(hào)「飛天小牛肉」,作者飛天小牛肉。轉(zhuǎn)載本文請(qǐng)聯(lián)系飛天小牛肉公眾號(hào)。

創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比北海網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式北海網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋北海地區(qū)。費(fèi)用合理售后完善,10多年實(shí)體公司更值得信賴。

集合這塊知識(shí)的重要性不用多說,加上多線程妥妥的穩(wěn)占面試必問霸主地主,深入了解集合框架的整體結(jié)構(gòu)以及各個(gè)集合類的實(shí)現(xiàn)原理是非常有必要的。

由于不同的集合在實(shí)現(xiàn)上采用了各種不同的數(shù)據(jù)結(jié)構(gòu),導(dǎo)致了各個(gè)集合的性能、底層實(shí)現(xiàn)、使用方式上存在一定的差異,所以集合這塊的知識(shí)點(diǎn)非常多,不過好在它的整體學(xué)習(xí)框架比較清晰。本文只籠統(tǒng)介紹集合框架的知識(shí)體系,幫助大家理清思路,重點(diǎn)集合類的詳細(xì)分析之后會(huì)單獨(dú)分成幾篇文章。

全文脈絡(luò)思維導(dǎo)圖如下:

1. 為什么要使用集合

當(dāng)我們?cè)趯W(xué)習(xí)一個(gè)東西的時(shí)候,最好是明白為什么要使用這個(gè)東西,不要為了用而用,知其然而知其所以然。

集合,故名思議,是用來存儲(chǔ)元素的,而數(shù)組也同樣具有這個(gè)功能,那么既然出現(xiàn)了集合,必然是因?yàn)椤笖?shù)組的使用存在一定的缺陷」。

上篇文章已經(jīng)簡單提到過,數(shù)組一旦被定義,就無法再更改其存儲(chǔ)大小。舉個(gè)例子,假設(shè)有一個(gè)班級(jí),現(xiàn)在有 50 個(gè)學(xué)生在這個(gè)班里,于是我們定義了一個(gè)能夠存儲(chǔ) 50 個(gè)學(xué)生信息的數(shù)組:

1)如果這個(gè)班里面來了 10 個(gè)轉(zhuǎn)班生,由于數(shù)組的長度固定不變,那么顯然這個(gè)數(shù)組的存儲(chǔ)能力無法支持 60 個(gè)學(xué)生;再比如,這個(gè)班里面有 20 個(gè)學(xué)生退學(xué)了,那么這個(gè)數(shù)組實(shí)際上只存了 30 個(gè)學(xué)生,造成了內(nèi)存空間浪費(fèi)??偨Y(jié)來說,「由于數(shù)組一旦被定義,就無法更改其長度,所以數(shù)組無法動(dòng)態(tài)的適應(yīng)元素?cái)?shù)量的變化」。

2)數(shù)組擁有 length 屬性,可以通過這個(gè)屬性查到數(shù)組的存儲(chǔ)能力也就是數(shù)組的長度,但是無法通過一個(gè)屬性直接獲取到數(shù)組中實(shí)際存儲(chǔ)的元素?cái)?shù)量。

3)因?yàn)椤笖?shù)組在內(nèi)存中采用連續(xù)空間分配的存儲(chǔ)方式」,所以我們可以根據(jù)下標(biāo)快速獲的取對(duì)應(yīng)的學(xué)生信息。比如我們?cè)跀?shù)組下標(biāo)為 2 的位置存入了某個(gè)學(xué)生的學(xué)號(hào) 111,那顯然,直接通過下標(biāo) 2 就能獲取學(xué)號(hào) 111。但是「如果反過來我們想要查找學(xué)號(hào) 111 的下標(biāo)呢」?數(shù)組原生是做不到的,這就需要使用各種查找算法了。

4)另外,假如我們想要存儲(chǔ)學(xué)生的姓名和家庭地址的一一對(duì)應(yīng)信息,數(shù)組顯然也是做不到的。

5)如果我們想在這個(gè)用來存儲(chǔ)學(xué)生信息的數(shù)組中存儲(chǔ)一些老師的信息,數(shù)組是無法滿足這個(gè)需求的,它只能存儲(chǔ)相同類型的元素。

為了解決這些數(shù)組在使用過程中的痛點(diǎn),集合框架應(yīng)用而生。簡單來說,集合的主要功能就是兩點(diǎn):

  • 存儲(chǔ)不確定數(shù)量的數(shù)據(jù)(可以動(dòng)態(tài)改變集合長度)
  • 存儲(chǔ)具有映射關(guān)系的數(shù)據(jù)
  • 存儲(chǔ)不同類型的數(shù)據(jù)

不過,需要注意的是,「集合只能存儲(chǔ)引用類型(對(duì)象),如果你存儲(chǔ)的是 int 型數(shù)據(jù)(基本類型),它會(huì)被自動(dòng)裝箱成 Integer 類型。而數(shù)組既可以存儲(chǔ)基本數(shù)據(jù)類型,也可以存儲(chǔ)引用類型」。

2. 集合框架體系速覽

與現(xiàn)代的數(shù)據(jù)結(jié)構(gòu)類庫的常見情況一樣,Java 集合類也將接口與實(shí)現(xiàn)分離,這些接口和實(shí)現(xiàn)類都位于 java.util 包下。按照其存儲(chǔ)結(jié)構(gòu)集合可以分為兩大類:

  • 單列集合 Collection
  • 雙列集合 Map

Collection 接口

「單列集合」 java.util.Collection:元素是孤立存在的,向集合中存儲(chǔ)元素采用一個(gè)個(gè)元素的方式存儲(chǔ)。

來看 Collection 接口的繼承體系圖:

Collection 接口中定義了一些單列集合通用的方法:

 
 
 
 
  1. public boolean add(E e); // 把給定的對(duì)象添加到當(dāng)前集合中 
  2. public void clear(); // 清空集合中所有的元素 
  3. public boolean remove(E e); // 把給定的對(duì)象在當(dāng)前集合中刪除 
  4. public boolean contains(E e); // 判斷當(dāng)前集合中是否包含給定的對(duì)象 
  5. public boolean isEmpty(); // 判斷當(dāng)前集合是否為空 
  6. public int size(); // 返回集合中元素的個(gè)數(shù) 
  7. public Object[] toArray(); // 把集合中的元素,存儲(chǔ)到數(shù)組中 

Collection 有兩個(gè)重要的子接口,分別是 List 和 Set,它們分別代表了有序集合和無序集合:

1)List 的特點(diǎn)是「元素有序、可重復(fù)」,這里所謂的有序意思是:「元素的存入順序和取出順序一致」。例如,存儲(chǔ)元素的順序是 11、22、33,那么我們從 List 中取出這些元素的時(shí)候也會(huì)按照 11、22、33 這個(gè)順序。List 接口的常用實(shí)現(xiàn)類有:

  • 「ArrayList」:底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,線程不安全
  • 「LinkedList」:底層數(shù)據(jù)結(jié)構(gòu)是鏈表,線程不安全

除了包括 Collection 接口的所有方法外,List 接口而且還增加了一些根據(jù)元素索引來操作集合的特有方法:

public void add(int index, E element); // 將指定的元素,添加到該集合中的指定位置上public E get(int index); // 返回集合中指定位置的元素public E remove(int index); // 移除列表中指定位置的元素, 返回的是被移除的元素public E set(int index, E element); // 用指定元素替換集合中指定位置的元素

2)Set 接口在方法簽名上與 Collection 接口其實(shí)是完全一樣的,只不過在方法的說明上有更嚴(yán)格的定義,最重要的特點(diǎn)是他「拒絕添加重復(fù)元素,不能通過整數(shù)索引來訪問」,并且「元素?zé)o序」。所謂無序也就是元素的存入順序和取出順序不一致。其常用實(shí)現(xiàn)類有:

  • 「HashSet」:底層基于 HashMap 實(shí)現(xiàn),采用 HashMap 來保存元素
  • 「LinkedHashSet」:LinkedHashSet 是 HashSet 的子類,并且其底層是通過 LinkedHashMap 來實(shí)現(xiàn)的。

至于為什么要定義一個(gè)方法簽名完全相同的接口,我的理解是為了讓集合框架的結(jié)構(gòu)更加清晰,將單列集合從以下兩點(diǎn)區(qū)分開來:

  • 可以添加重復(fù)元素(List)和不可以添加重復(fù)元素(Set)
  • 可以通過整數(shù)索引訪問(List)和不可以通過整數(shù)索引(Set)

這樣當(dāng)我們聲明單列集合時(shí)能夠更準(zhǔn)確的繼承相應(yīng)的接口。

Map 接口

「雙列集合」 java.util.Map:元素是成對(duì)存在的。每個(gè)元素由鍵(key)與值(value)兩部分組成,通過鍵可以找對(duì)所對(duì)應(yīng)的值。顯然這個(gè)雙列集合解決了數(shù)組無法存儲(chǔ)映射關(guān)系的痛點(diǎn)。另外,需要注意的是,「Map 不能包含重復(fù)的鍵,值可以重復(fù);并且每個(gè)鍵只能對(duì)應(yīng)一個(gè)值」。

來看 Map 接口的繼承體系圖:

Map 接口中定義了一些雙列集合通用的方法:

 
 
 
 
  1. public V put(K key, V value); // 把指定的鍵與指定的值添加到 Map 集合中。 
  2. public V remove(Object key); // 把指定的鍵所對(duì)應(yīng)的鍵值對(duì)元素在 Map 集合中刪除,返回被刪除元素的值。 
  3. public V get(Object key); // 根據(jù)指定的鍵,在 Map 集合中獲取對(duì)應(yīng)的值。 
  4. boolean containsKey(Object key); // 判斷集合中是否包含指定的鍵。 
  5. public Set keySet(); // 獲取 Map 集合中所有的鍵,存儲(chǔ)到 Set 集合中。 

Map 有兩個(gè)重要的實(shí)現(xiàn)類,HashMap 和 LinkedHashMap :

① 「HashMap」:可以說 HashMap 不背到滾瓜爛熟不敢去面試,這里簡單說下它的底層結(jié)構(gòu),后面會(huì)開文詳細(xì)講解。JDK 1.8 之前 HashMap 底層由數(shù)組加鏈表實(shí)現(xiàn),數(shù)組是 HashMap 的主體,鏈表則是主要為了解決哈希沖突而存在的(“拉鏈法” 解決沖突)。JDK1.8 以后在解決哈希沖突時(shí)有了較大的變化,當(dāng)鏈表長度大于閾值(默認(rèn)為 8)時(shí),將鏈表轉(zhuǎn)化為紅黑樹,以減少搜索時(shí)間(注意:將鏈表轉(zhuǎn)換成紅黑樹前會(huì)判斷,如果當(dāng)前數(shù)組的長度小于 64,那么會(huì)選擇先進(jìn)行數(shù)組擴(kuò)容,而不是轉(zhuǎn)換為紅黑樹)。

② 「LinkedHashMap」:HashMap 的子類,可以保證元素的存取順序一致(存進(jìn)去時(shí)候的順序是多少,取出來的順序就是多少,不會(huì)因?yàn)?key 的大小而改變)。

LinkedHashMap 繼承自 HashMap,所以它的底層仍然是基于拉鏈?zhǔn)缴⒘薪Y(jié)構(gòu),即由數(shù)組和鏈表或紅黑樹組成。另外,LinkedHashMap 在上面結(jié)構(gòu)的基礎(chǔ)上,增加了一條雙向鏈表,使得上面的結(jié)構(gòu)可以保持鍵值對(duì)的插入順序。同時(shí)通過對(duì)鏈表進(jìn)行相應(yīng)的操作,實(shí)現(xiàn)了訪問順序相關(guān)邏輯。

OK,我們已經(jīng)知道,Map中存放的是兩種對(duì)象,一種稱為 key(鍵),一種稱為 value(值),它倆在 Map 中是一一對(duì)應(yīng)關(guān)系,這一對(duì)對(duì)象又稱做 Map 中的一個(gè) 「Entry」(項(xiàng))。Entry 將鍵值對(duì)的對(duì)應(yīng)關(guān)系封裝成了對(duì)象,即鍵值對(duì)對(duì)象。Map 中也提供了獲取所有 Entry 對(duì)象的方法:

 
 
 
 
  1. public Set> entrySet(); // 獲取 Map 中所有的 Entry 對(duì)象的集合。 

同樣的,Map 也提供了獲取每一個(gè) Entry 對(duì)象中對(duì)應(yīng)鍵和對(duì)應(yīng)值的方法,這樣我們?cè)诒闅v Map 集合時(shí),就可以從每一個(gè)鍵值對(duì)(Entry)對(duì)象中獲取對(duì)應(yīng)的鍵與對(duì)應(yīng)的值了:

 
 
 
 
  1. public K getKey(); // 獲取某個(gè) Entry 對(duì)象中的鍵。 
  2. public V getValue(); // 獲取某個(gè) Entry 對(duì)象中的值。 

下面我們結(jié)合上述所學(xué),來看看 Map 的兩種遍歷方式:

1)「遍歷方式一:根據(jù) key 找值方式」

  • 獲取 Map 中所有的鍵,由于鍵是唯一的,所以返回一個(gè) Set 集合存儲(chǔ)所有的鍵。方法提示:keyset()
  • 遍歷鍵的 Set 集合,得到每一個(gè)鍵。
  • 根據(jù)鍵,獲取鍵所對(duì)應(yīng)的值。方法提示:get(K key)
 
 
 
 
  1. public static void main(String[] args) { 
  2.     // 創(chuàng)建 Map 集合對(duì)象  
  3.     HashMap map = new HashMap(); 
  4.     // 添加元素到集合  
  5.     map.put(1, "小五"); 
  6.     map.put(2, "小紅"); 
  7.     map.put(3, "小張"); 
  8.  
  9.     // 獲取所有的鍵  獲取鍵集 
  10.     Set keys = map.keySet(); 
  11.     // 遍歷鍵集 得到 每一個(gè)鍵 
  12.     for (Integer key : keys) { 
  13.         // 獲取對(duì)應(yīng)值 
  14.         String value = map.get(key); 
  15.         System.out.println(key + ":" + value); 
  16.     }   

這里面不知道大家有沒有注意一個(gè)細(xì)節(jié),keySet 方法的返回結(jié)果是 Set。Map 由于沒有實(shí)現(xiàn) Iterable 接口,所以不能直接使用迭代器或者 for each 循環(huán)進(jìn)行遍歷,但是轉(zhuǎn)成 Set 之后就可以使用了。至于迭代器是啥請(qǐng)繼續(xù)往下看。

2)「遍歷方式二:鍵值對(duì)方式」

  • 獲取 Map 集合中,所有的鍵值對(duì) (Entry) 對(duì)象,以 Set 集合形式返回。方法提示:entrySet()。
  • 遍歷包含鍵值對(duì) (Entry) 對(duì)象的 Set 集合,得到每一個(gè)鍵值對(duì) (Entry) 對(duì)象。
  • 獲取每個(gè) Entry 對(duì)象中的鍵與值。方法提示:getkey()、getValue()
 
 
 
 
  1. // 獲取所有的 entry 對(duì)象 
  2. Set> entrySet = map.entrySet(); 
  3.  
  4. // 遍歷得到每一個(gè) entry 對(duì)象 
  5. for (Entry entry : entrySet) { 
  6.     Integer key = entry.getKey(); 
  7.     String value = entry.getValue();   
  8.     System.out.println(key + ":" + value); 

3. 迭代器 Iterator什么是 Iterator

在上一章數(shù)組中我們講過 for each 循環(huán):

 
 
 
 
  1. for(variable : collection) { 
  2.     // todo 

collection 這一表達(dá)式必須是一個(gè)數(shù)組或者是一個(gè)實(shí)現(xiàn)了 Iterable 接口的類對(duì)象??梢钥吹?Collection 這個(gè)接口就繼承了 Itreable 接口,所以所有實(shí)現(xiàn)了Collection 接口的集合都可以使用 for each 循環(huán)。

我們點(diǎn)進(jìn) Iterable 中看一看:

它擁有一個(gè) iterator 方法,返回類型是 Iterator,這又是啥,我們?cè)冱c(diǎn)進(jìn)去看看:

又是三個(gè)接口,不過無法再跟下去了,我們?nèi)?Collection 的實(shí)現(xiàn)類中看看,有沒有實(shí)現(xiàn) Itreator 這個(gè)接口,隨便打開一個(gè),比如 ArrayList :

從源碼可知:Iterator 接口在 ArrayList 中是以「內(nèi)部類」的方式實(shí)現(xiàn)的。并且,Iterator 實(shí)際上就是在遍歷集合。

所以總結(jié)來說:我們可以通過 Iterator 接口遍歷 Collection 的元素,這個(gè)接口的具體實(shí)現(xiàn)是在具體的子類中,以內(nèi)部類的方式實(shí)現(xiàn)。

 這里提個(gè)問題,「為什么迭代器不封裝成一個(gè)類,而是做成一個(gè)接口」?假設(shè)迭代器是一個(gè)類,這樣我們就可以創(chuàng)建該類的對(duì)象,調(diào)用該類的方法來實(shí)現(xiàn) Collection的遍歷。

但事實(shí)上,Collection 接口有很多不同的實(shí)現(xiàn)類,在文章開頭我們就說過,這些類的底層數(shù)據(jù)結(jié)構(gòu)大多是不一樣的,因此,它們各自的存儲(chǔ)方式和遍歷方式也是不同的,所以我們不能用一個(gè)類來規(guī)定死遍歷的方法。我們提取出遍歷所需要的通用方法,封裝進(jìn)接口中,讓 Collection 的子類根據(jù)自己自身的特性分別去實(shí)現(xiàn)它。

看完上面這段分析,我們來驗(yàn)證一下,看看 LinkedList 實(shí)現(xiàn)的 Itreator 接口和 ArrayList 實(shí)現(xiàn)的是不是不一樣:

顯然,這兩個(gè)雖然同為 Collection 的實(shí)現(xiàn)類,但是它們具體實(shí)現(xiàn) Itreator 接口的內(nèi)部過程是不一樣的。

Iterator 基本使用

OK,我們已經(jīng)了解了 Iterator 是用來遍歷 Collection 集合的,那么具體是怎么遍歷的呢?

答:「迭代遍歷」!

解釋一下迭代的概念:在取元素之前先判斷集合中有沒有元素,如果有,就把這個(gè)元素取出來,再繼續(xù)判斷,如果還有就再繼續(xù)取出來。一直到把集合中的所有元素全部取出。這種取出方式就稱為迭代。因此Iterator 對(duì)象也被稱為「迭代器」。

也就是說,想要遍歷 Collection 集合,那么就要獲取該集合對(duì)應(yīng)的迭代器。如何獲取呢?其實(shí)上文已經(jīng)出現(xiàn)過了,Collection 實(shí)現(xiàn)的 Iterable 中就有這樣的一個(gè)方法:iterator

再來介紹一下 Iterator 接口中的常用方法:

 
 
 
 
  1. public E next(); // 返回迭代的下一個(gè)元素。 
  2. public boolean hasNext(); // 如果仍有元素可以迭代,則返回 true 

舉個(gè)例子:

 
 
 
 
  1. public static void main(String[] args) { 
  2.     Collection coll = new ArrayList(); 
  3.  
  4.     // 添加元素到集合 
  5.     coll.add("A"); 
  6.     coll.add("B"); 
  7.     coll.add("C"); 
  8.     // 獲取 coll 的迭代器 
  9.     Iterator it = coll.iterator(); 
  10.     while(it.hasNext()){ // 判斷是否有迭代元素 
  11.         String s = it.next(); // 獲取迭代出的元素 
  12.         System.out.println(s); 
  13.     } 

當(dāng)然,用 for each 循環(huán)可以更加簡單地表示同樣的循環(huán)操作:

 
 
 
 
  1. Collection coll = new ArrayList(); 
  2. ... 
  3. for(String element : coll){ 
  4.     System.out.println(element); 

References

  • 《Java 核心技術(shù) - 卷 1 基礎(chǔ)知識(shí) - 第 10 版》
  • Java3y - 集合Collection總覽:https://juejin.cn/post/6844903587441541127#heading-1

當(dāng)前題目:Java集合框架體系總覽
標(biāo)題來源:http://www.5511xx.com/article/dhpjooj.html