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 operator) 對每個元素執行制定操作,並用結果代替對應元素

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 succ) 將新元素添加到指定節點的前面
peek() 檢索並返回鏈表的首個節點
peekFirst() 檢索並返回鏈表的首個節點
peekLast() 檢索並返回鏈表的尾端節點
poll() 檢索返回並移除鏈表的首個節點
pollFirst() 檢索返回並移除鏈表的首個節點
pollLast() 檢索返回並移除鏈表的尾端節點
pop() 將第一個元素返回(出棧),並將其從列表移除
push(E e) 將一個元素添加到鏈表首個節點(入棧)
clear() 循環設置節點爲null,最後設置first和last節點爲null並將鏈表的size設置爲0
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) 獲取子列表
removeLastOccurrence(Object o) 刪除對象o最後一次出現位置的節點
removeFirstOccurrence(Object o) 刪除對象o第一次出現位置的節點

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集合框架綜述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章