1. 程式人生 > >Java集合系列之List介面

Java集合系列之List介面

List是一個有序的佇列,每一個元素都有它的索引。第一個元素的索引值是0。List的實現類有LinkedList, ArrayList, Vector, Stack。

List抽象資料型別:

ADT
List

Data
線性表的元素集合為{a1,a2,a3,a4....an},資料型別都是DataType.線性表的元素可以重複,並且可以插入null。

Operations(僅定義邏輯方法,與JDK方法不保持一致)
InitList(); 初始化操作,建立一個空線性表。
ListEmpty(); 判斷線性表是否為空,返回布林值。
AddElement(DataType d,I i); 線上性表的位置i插入元素d。
GetElement(I i); 獲取線性表在位置i的元素。
LocateElement(DataType d); 獲取元素d線上性表的位置。
RemoveElement(DataType d);/RemoveElement(I i); 移除元素d(在位置i的元素)。
ClearList(); 清空線性表。
ListLength(); 線性表的大小。

List介面繼承於Collection介面,它可以定義一個允許重複的有序集合。因為List中的元素是有序的,所以我們可以通過使用索引(元素在List中的位置,類似於陣列下標)來訪問List中的元素,這類似於Java的陣列。

Collection介面

List介面
方法剖析

  • add (E e) 向列表內新增指定元素
  • add(int index.E e) 向集合指定位置新增元素
  • addAll(Collection<? extends E> c) addAll(int index, Collection<? extends E> c) 向集合內(指定位置 index)新增另一集合的全部元素
  • get(int index)獲取指定位置的元素
  • clear() 清空集合,可用於多次使用單個集合物件,節省資源
  • contains(Object o) 判斷是否還有某個物件
  • containsAll(Collection<?> c) 判斷是否含有集合c的所有元素
  • parallelStream() java8新特性,使用fork/join框架自動並行處理
  • remove(Object o) 移除集合中的一個元素
  • removeAll(Collection<?> c) 移除集合含有的集合c中的所有元素
  • removeIf(Predicate<? super E> filter) 底層迭代呼叫Predicate的test方法。Predicate函式式介面主要用提供test()方法,該方法返回一個布林變數
  • spliterator() 並行迭代器
  • stream() 返回集合的流資源,用於函式式運算
  • toArray() 轉換成陣列
  • toArray(T[] a) 將集合轉換成對應物件型別的陣列
  • sort(Comparator<? super E> c) 使用函數語言程式設計對列表進行排序,排序通過
    Arrays.sort()實現
  • subList(int fromIndex, int toIndex) 獲取子列表
  • retainAll(Collection<?> c) 移除除c元素的所有其他元素
  • replaceAll(UnaryOperator

List介面為Collection子介面。List所代表的是有序的Collection,即它用某種特定的插入順序來維護元素順序。使用者可以對列表中每個元素的插入位置進行精確地控制,同時可以根據元素的整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素。實現List介面的集合主要有:ArrayList、LinkedList、Vector、Stack

ArrayList類

ArrayList是一個動態陣列,也是我們最常用的集合。它允許任何符合規則的元素插入甚至包括null。每一個ArrayList都有一個初始容量(10),該容量代表了陣列的大小。隨著容器中的元素不斷增加,容器的大小也會隨著增加。在每次向容器中增加元素的同時都會進行容量檢查,當快溢位時,就會進行擴容操作(自動擴容機制)。所以如果我們明確所插入元素的多少,最好指定一個初始容量值,避免過多的進行擴容操作而浪費時間、效率。

size、isEmpty、get、set、iterator 和 listIterator 操作的演算法複雜度。add 操作以分攤的固定時間執行,也就是說,新增 n 個元素需要 O(n) 時間(由於要考慮到擴容,所以這不只是新增元素會帶來分攤固定時間開銷那樣簡單)。

ArrayList擅長於隨機訪問。同時ArrayList是非同步的。
如果多執行緒訪問ArrayList,可以建立一個執行緒安全的ArrayList:
List list = Collections.synchronizedList(new ArrayList(...));

ArrayList抽象資料型別:

ADT  ArrayList

Data 資料集合為{a1,a2,a3....,an},元素的儲存順尋和放進去的順序保持一致,底層資料放在一個Object[]陣列中,
     具有一個容量CAPACITY,當新增元素時,會先檢查容量,如果容量不足,執行自動擴容操作。

Operatons(僅定義邏輯方法,與JDK方法不保持一致)
InitList(); 初始化操作,建立一個空線性表。
InitList(n); 初始化操作,建立一個容量為n的陣列。
InitList(Collection c);初始化操作,使用Collection的元素建立一個新的陣列表,兩者的DataType必須一致。
ListEmpty(); 判斷線性表是否為空,返回布林值。
AddElement(DataType d,I i); 線上性表的位置i插入元素d。
GetElement(I i); 獲取線性表在位置i的元素。
LocateElement(DataType d); 獲取元素d線上性表的位置。
RemoveElement(DataType d);/RemoveElement(I i); 移除元素d(在位置i的元素)。
SetElement(DataType d,I i); 將位置i的元素設定為d;
ClearList(); 清空線性表。
ListLength(); 線性表的大小。

方法剖析
add (E e) 向集合內新增指定元素,ArrayList具有自動擴容機制,當新增元素時,會自動擴充套件ArrayList內部儲存資料的elementData陣列大小,
最大儲存容量為int的儲存範圍(<= 2147483647),並將新元素放於陣列的尾部(有序儲存)
add(int index, E element) 底層實現使用了System.arraycopy,新增新元素到陣列列表,預設新增到陣列的最後
addAll(Collection<? extends E> c) addAll(int index, Collection<?extends E> c) 向集合內新增另一集合的全部元素,同樣使用自動擴容機制.由於底層實現使用了System.arraycopy,基於淺複製(複製的是物件的引用,改變任何一個物件都會影響另一個),所以是執行緒不安全的.
get(int index)獲取指定位置的元素
clear() 迴圈設定底層儲存陣列elementData[]為null,最後設定ArryList陣列size為0.會設定++modCount,用於保證迭代器執行緒安全.
contains(Object o) 判斷是否含有某個物件,底層實現為呼叫indexOf(Object var1),如果對應的索引值大於等於0,則含有該物件
containsAll(Collection<?> c) 判斷是否含有集合c的所有元素,底層實現為迴圈呼叫contains(Object o).由ArrayList的父類AbstractList的父類AbstractCollection實現,ArrayList直接呼叫父類方法.
parallelStream() 使用Fork/Join框架並行執行任務,涉及到執行緒池和工作竊取演算法
remove(int index) 底層使用System.arraycopy,將index之後的子陣列全部前移一位
remove(Object o) 遍歷底層陣列,找到對應的元素後呼叫remove(int index)移除元素
removeAll(Collection<?> c)移除集合含有的集合c中的所有元素,底層實現為使用contains(Object o)遍歷,移除兩個集合的交集。演算法複雜度為O(m*n),當資料量較大時,會影響效能。優化可參考:Java中ArrayList的removeAll方法詳解
removeIf(Predicate<?super E> filter) 底層迭代呼叫Predicate的test方法。Predicate函式式介面主要用提供test()方法,該方法返回一個布林變數。Collection介面已經實現,直接呼叫。
spliterator() 並行迭代器
stream() 返回集合的流資源,用於函式式運算
toArray() 轉換成陣列
toArray(Collection<?extends E> c) 將集合轉換成Object物件型別的陣列
subList(int fromIndex, int toIndex) 獲取子列表,由父類AbstractSequentialList實現

LinkedList

同樣實現List介面的LinkedList與ArrayList不同,ArrayList是一個動態陣列,而LinkedList是一個雙向連結串列。所以它除了有ArrayList的基本操作方法外還額外提供了get,remove,insert方法在LinkedList的首部或尾部。

由於實現的方式不同,LinkedList不能隨機訪問,它所有的操作都是要按照雙重連結串列的需要執行。在列表中索引的操作將從開頭或結尾遍歷列表(從靠近指定索引的一端)。這樣做的好處就是可以通過較低的代價在List中進行插入和刪除操作。

與ArrayList一樣,LinkedList也是非同步的。如果多個執行緒同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在建立List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(...));

LinkedList抽象資料型別

ADT LinkedList

Data  線性連結串列的資料物件集合為{a1,a2,...an},每個元素的型別均為DataType。
除了第一個元素外,每一個元素有且僅有一個直接前驅元素,除最後一個元素外,每個元素有且僅有一個直接後繼元素。

Operations
initList()初始化操作
listEmpty()判斷線性表是否為空
clearList()清空線性表
contains(DataType d)是否包含某個元素
getElement(I i)將第i個位置的值返回給e
locateElement(DataType e)線上性表中查詢與e相同的值的位置
insert(DataTypy e)插入元素到線性表,預設插入到列表的最後位置
listInsert(Ii,DataType e) 插入操作,線上性表的第i個位置插入新元素
delete(DataType e) 刪除操作
iteratorList()遍歷連結串列

方法剖析
add (E e) 向集合內新增指定元素,預設將新元素新增到連結串列的末端
add(int index, E element) 將元素插入到連結串列對應的位置,改變其前驅和後驅元素,將新元素加入
addAll(Collection<?extends E> c) addAll(int index, Collection<?extends E> c) 向集合內新增另一集合的全部元素,同樣使用自動擴容機制.由於底層實現使用了System.arraycopy,基於淺複製(複製的是物件的引用,改變任何一個物件都會影響另一個),所以是執行緒不安全的.
addFirst(E e) 將元素新增到連結串列的起始位置
addLast(E e) 將元素新增到連結串列的末尾位置
get(int index)獲取指定位置的元素,底層實現為遍歷連結串列,時間複雜度為O(n),遠大於ArrayList的O(1)
linkFirst(E e) 將新元素新增到連結串列的首端
linkLast(E e) 將新元素新增到連結串列的末端
linkBefore(E e, Node

Vector

與ArrayList相似,但是Vector是同步的。所以說Vector是執行緒安全的動態陣列。它的操作與ArrayList幾乎一樣。但是Vector由於使用了synchronized標識所有的方法,所以效能上沒有ArrayList高。

Stack

抽象元素型別

ADT Stack

Data 棧的資料集合為{a1,a2,a3....an},每個元素的型別均為DataType。
棧具有後進先出的特性last-in-first-out (LIFO),後進的元素先出棧。

Operations
InitStack(); 初始化一個空棧
ClearStack(); 清空棧
StackEmpty(); 判斷當前棧是否為空
push(); 將元素壓入棧
pop(); 將棧頂元素出棧

Stack繼承自Vector,實現一個後進先出的堆疊,底層是一個數組。Stack提供5個額外的方法使得Vector得以被當作堆疊使用。基本的push和pop 方法,還有peek方法得到棧頂的元素,empty方法測試堆疊是否為空,search方法檢測一個元素在堆疊中的位置。Stack剛建立後是空棧。

[1]: 搞懂 Java ArrayList 原始碼
[2]: java集合框架綜述