JavaSE中Collection集合框架學習筆記(1)——具有索引的List


前言:因為最近要重新找工作,Collection(集合)是面試中出現頻率非常高的基礎考察點,所以好好惡補了一番。

 

復習過程中深感之前的學習不系統,而且不能再像剛畢業那樣死背面試題,例如:String是固定長度的,StringBuffer和StringBuilder的長度是可以變化的。如果一旦問得深入一點,問為什么有這樣的區別就傻眼了,只能一臉呆萌地看着面試官。

 

因此想要通過寫文章的形式,系統地總結學習的內容,例如Collection架構是怎樣的、有哪些相關的繼承和接口實現,這樣才能了解什么時候應該用哪個類,以及類之間要如何搭配合作,才知道出了問題應該如何解決。這一系列文章適用於Java技術崗的應聘者、高校計算機專業的學生以及培訓機構學習Java的初學者閱讀。

 

1.1 認識Collection架構

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

 

 

ArrayList一個類就這么復雜,如果要把全部Collection架構表現在一張圖上,那估計就跟蜘蛛網一樣糾纏不清。簡化一下,忽略一些不那么重要的接口和實現類,我們可以得到以下這張架構圖。

 

 

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

 

這張圖將會是這一系列文章的核心,之后會反復提及,不妨稱之為Collection架構圖,每一篇文章都是介紹其中的一部分。熟悉這張圖,不僅有助於理解學習,還可以幫助記憶。至於詳細周全的繼承關系和實現架構,到底有哪些類實現了哪些接口、繼承了哪些類,可以自行在API說明文檔中查詢。

 

1.2 具有索引的List

List實現了Collection接口,所以我們可以說List是一種Colletion,作用就是收集對象,特點是以索引的方式記錄所收集的對象順序。List中常見的實現類是剛才所提及架構圖中的ArrayList,忘了的讀者可以翻到前面對照着看。

 1 /**
 2  * ArrayList的實驗用例  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和ArrayList
10         Scanner scanner = new Scanner(System.in); 11  String name; 12         while(true) { 13             System.out.print("學生簽到:"); 14             name = scanner.nextLine(); 15             if(name.equals("quit")) { 16                 break; 17  } 18             list.add(name); //實用Add()方法收集對象
19  } 20         System.out.println("今天上來上課的學生名單:"); 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()方法依據索引取得收集的對象
27  System.out.println(student); 28  } 29  } 30 }

 

以上是ArrayList類的一個簡單使用例子,模擬的是學生上課簽到的情景。強烈建議讀者跟我一樣自己試着寫一個簡單用例,尤其是之前很少使用ArrayList的初學者,單純的看和讀跟實際敲代碼產生的效果完全不一樣。也可以照着我給出的例子敲,偷懶一點的話可以直接復制在機器上跑一遍。

 

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

 

1.2.1 ArrayList的特性

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

 

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

 

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

 

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

 

1.2.2 LinkedList的特性

在學習Collection架構的時候,我們不妨可以多看源代碼,看的時候優先比較幾個基本方法的實現,例如add()、remove()等。從這些方法的實現,我們就可以看到不同實現類的特性。

    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++; }

 

看到LinkdedList.add()的源代碼,我們會發現其實現方式跟鏈表的實現如出一轍。如果last結點為null,那么說明鏈表為空,所以新添加的結點為頭結點。如果last結點不等於null,那么把新添加的結點設為last的下一個結點,作為新的尾結點。

 

根據鏈表的特性,我們可以很快總結兩點點特性:1.想要指定索引隨機存取時,鏈接方式都得使用從第一個元素開始查找下一個元素的方式,效率比較糟糕;2.鏈接的每個元素都會參考下一個元素,這有利於調整索引順序。

 

1.2.3 List總結

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

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

 

面試中常見的List實現類其實還有Vector,其特性與ArrayList相同。不同在於Vector具有線程安全的特性,性能開銷比較大,具體的內容會放在以后關於多線程的文章里。

 

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

 

 

 

如果你喜歡我的文章,可以掃描關注我的個人公眾號“李文業的思考筆記”。

不定期地會推送我的原創思考文章。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM