前言:因?yàn)樽罱匦抡夜ぷ?,Collection(集合)是面試中出現(xiàn)頻率非常高的基礎(chǔ)考察點(diǎn),所以好好惡補(bǔ)了一番。

 

復(fù)習(xí)過程中深感之前的學(xué)習(xí)不系統(tǒng),而且不能再像剛畢業(yè)那樣死背面試題,例如:String是固定長度的,StringBuffer和StringBuilder的長度是可以變化的。如果一旦問得深入一點(diǎn),問為什么有這樣的區(qū)別就傻眼了,只能一臉呆萌地看著面試官。

 

因此想要通過寫文章的形式,系統(tǒng)地總結(jié)學(xué)習(xí)的內(nèi)容,例如Collection架構(gòu)是怎樣的、有哪些相關(guān)的繼承和接口實(shí)現(xiàn),這樣才能了解什么時(shí)候應(yīng)該用哪個(gè)類,以及類之間要如何搭配合作,才知道出了問題應(yīng)該如何解決。這一系列文章適用于Java技術(shù)崗的應(yīng)聘者、高校計(jì)算機(jī)專業(yè)的學(xué)生以及培訓(xùn)機(jī)構(gòu)學(xué)習(xí)Java的初學(xué)者閱讀。

 

1.1 認(rèn)識(shí)Collection架構(gòu)

我們都使用過ArrayList類收集對(duì)象,例如add()方法新增對(duì)象,remove()方法移除對(duì)象,這些都不會(huì)陌生。但是這些方法是怎么來的呢?下圖是該類的繼承架構(gòu)圖:

 

電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 

ArrayList一個(gè)類就這么復(fù)雜,如果要把全部Collection架構(gòu)表現(xiàn)在一張圖上,那估計(jì)就跟蜘蛛網(wǎng)一樣糾纏不清。簡(jiǎn)化一下,忽略一些不那么重要的接口和實(shí)現(xiàn)類,我們可以得到以下這張架構(gòu)圖。

 

電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 

從圖上可知,Colletion是一個(gè)接口,實(shí)現(xiàn)了另一個(gè)接口Iterable。Collection下面有三個(gè)接口直接實(shí)現(xiàn)了它,分別是List、Set和Queue。List下面有兩個(gè)實(shí)現(xiàn)類,分別是ArrayList和LinkedList;Set的常用實(shí)現(xiàn)類是TreeSet和HashSet;Queue下面有Deque接口實(shí)現(xiàn),再下面是實(shí)現(xiàn)類ArrayDeque.

 

這張圖將會(huì)是這一系列文章的核心,之后會(huì)反復(fù)提及,不妨稱之為Collection架構(gòu)圖,每一篇文章都是介紹其中的一部分。熟悉這張圖,不僅有助于理解學(xué)習(xí),還可以幫助記憶。至于詳細(xì)周全的繼承關(guān)系和實(shí)現(xiàn)架構(gòu),到底有哪些類實(shí)現(xiàn)了哪些接口、繼承了哪些類,可以自行在API說明文檔中查詢。

 

1.2 具有索引的List

List實(shí)現(xiàn)了Collection接口,所以我們可以說List是一種Colletion,作用就是收集對(duì)象,特點(diǎn)是以索引的方式記錄所收集的對(duì)象順序。List中常見的實(shí)現(xiàn)類是剛才所提及架構(gòu)圖中的ArrayList,忘了的讀者可以翻到前面對(duì)照著看。

電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 1 /** 2  * ArrayList的實(shí)驗(yàn)用例 3  */ 4  5 import java.util.*; 6  7 public class Student { 8     public static void main(String[] args) { 9         List list = new ArrayList(); //使用JavaSE的List和ArrayList10         Scanner scanner = new Scanner(System.in);11         String name;12         while(true) {13             System.out.print("學(xué)生簽到:");14             name = scanner.nextLine();15             if(name.equals("quit")) {16                 break;17             }18             list.add(name); //實(shí)用Add()方法收集對(duì)象19         }20         System.out.println("今天上來上課的學(xué)生名單:");21         foreach(list);22     }23 24     private static void foreach(List list) {25         for(int i = 0; i < list.size(); i++) {26             String student = (String) list.get(i); //使用get()方法依據(jù)索引取得收集的對(duì)象27             System.out.println(student);28         }29     }30 }

電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 

以上是ArrayList類的一個(gè)簡(jiǎn)單使用例子,模擬的是學(xué)生上課簽到的情景。強(qiáng)烈建議讀者跟我一樣自己試著寫一個(gè)簡(jiǎn)單用例,尤其是之前很少使用ArrayList的初學(xué)者,單純的看和讀跟實(shí)際敲代碼產(chǎn)生的效果完全不一樣。也可以照著我給出的例子敲,偷懶一點(diǎn)的話可以直接復(fù)制在機(jī)器上跑一遍。

電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 

從Collection架構(gòu)圖中可知,LinkedList同樣也實(shí)現(xiàn)了List接口。就算只是把上面那個(gè)實(shí)驗(yàn)中的ArrayList全部改為LinkedList,程序照樣可以運(yùn)作,而且效果看起來完全相同。那么問題來了,我們什么時(shí)候應(yīng)該使用ArrayList,什么時(shí)候又應(yīng)該使用LinkedList呢?

 

1.2.1 ArrayList的特性

卡車和輪船都可以運(yùn)送貨物,我們可以根據(jù)不同的情況選擇不同的運(yùn)輸方式。如果時(shí)間緊、運(yùn)輸量小,而且兩個(gè)地點(diǎn)都在陸地上(例如北京到南京),那么我們可以使用汽車;如果時(shí)間多、運(yùn)輸量大,出發(fā)地和目的之間隔著海洋(例如大連到紐約),那么用船運(yùn)是更好的選擇。

 

剛畢業(yè)那會(huì)要找工作,為了面試背過“ArrayList像數(shù)組,讀取速度快,但是需要調(diào)整索引的話表現(xiàn)很差;LinkedList像鏈表,調(diào)整索引的表現(xiàn)非常好,但是隨機(jī)讀取的速度比較慢”。那么我們可以問深一句,為什么會(huì)這樣呢?不妨從源代碼中找尋答案。

 

1     public boolean add(E e) {2         ensureCapacityInternal(size + 1);  // Increments modCount!!3         elementData[size++] = e;4         return true;5     }

 

上面這一段是JavaSE的源代碼,我們可以看到ArrayList中的add()方法非常簡(jiǎn)單,跟我們平時(shí)使用數(shù)組一樣。查看源代碼中更多內(nèi)容你會(huì)發(fā)現(xiàn),ArrayList內(nèi)部就是使用Object數(shù)組來保存所收集的對(duì)象,這就是為什么說“ArrayList就像數(shù)組”的原因。在考慮是否使用ArrayList的時(shí)候,我們可以相當(dāng)于考慮是否要使用數(shù)組的特性。

 

1.2.2 LinkedList的特性

在學(xué)習(xí)Collection架構(gòu)的時(shí)候,我們不妨可以多看源代碼,看的時(shí)候優(yōu)先比較幾個(gè)基本方法的實(shí)現(xiàn),例如add()、remove()等。從這些方法的實(shí)現(xiàn),我們就可以看到不同實(shí)現(xiàn)類的特性。

電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

    public boolean add(E e) {
        linkLast(e);        return true;
    }    /**
     * Links e as last element.     */
    void linkLast(E e) {        final Node<E> l = last;        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;        if (l == null)
            first = newNode;        else
            l.next = newNode;
        size++;
        modCount++;
    }

電腦培訓(xùn),計(jì)算機(jī)培訓(xùn),平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),Web培訓(xùn),Web前端開發(fā)培訓(xùn)

 

看到LinkdedList.add()的源代碼,我們會(huì)發(fā)現(xiàn)其實(shí)現(xiàn)方式跟鏈表的實(shí)現(xiàn)如出一轍。如果last結(jié)點(diǎn)為null,那么說明鏈表為空,所以新添加的結(jié)點(diǎn)為頭結(jié)點(diǎn)。如果last結(jié)點(diǎn)不等于null,那么把新添加的結(jié)點(diǎn)設(shè)為last的下一個(gè)結(jié)點(diǎn),作為新的尾結(jié)點(diǎn)。

 

根據(jù)鏈表的特性,我們可以很快總結(jié)兩點(diǎn)點(diǎn)特性:1.想要指定索引隨機(jī)存取時(shí),鏈接方式都得使用從第一個(gè)元素開始查找下一個(gè)元素的方式,效率比較糟糕;2.鏈接的每個(gè)元素都會(huì)參考下一個(gè)元素,這有利于調(diào)整索引順序。

 

1.2.3 List總結(jié)

作為Collection三大陣營之一的List,最大的特點(diǎn)就是索引,我們可以通過索引做到隨機(jī)存取。

List中常用的實(shí)現(xiàn)有ArrayList和LinkedList,各自的特性可以分別參考數(shù)組和鏈表。在比較它們之間區(qū)別的過程中,我們看了源代碼,提倡在比較同一接口不同實(shí)現(xiàn)類時(shí)重點(diǎn)查看它們共同需要實(shí)現(xiàn)的方法,例如Collection中規(guī)定的add(),remove()等。

 

面試中常見的List實(shí)現(xiàn)類其實(shí)還有Vector,其特性與ArrayList相同。不同在于Vector具有線程安全的特性,性能開銷比較大,具體的內(nèi)容會(huì)放在以后關(guān)于多線程的文章里。

 

在總結(jié)過程中,給初學(xué)者提供兩個(gè)建議,一是做實(shí)驗(yàn),通過寫一些demo來熟悉所學(xué)內(nèi)容;二是在力所能及的情況下多看源代碼,知其然也要知其所以然。

 

 

 

如果你喜歡我的文章,可以掃描關(guān)注我的個(gè)人公眾號(hào)“李文業(yè)的思考筆記”。

不定期地會(huì)推送我的原創(chuàng)思考文章。

 

http://www.cnblogs.com/levenyes/p/7117559.html