JavaSE 學習 —— 淺談 List
List 簡介
List 是 java.util 包下面的類,是一個有序集合(有時被稱為序列),List 可以包含重複的元素,其繼承了 Collection 的操作。除此之外,List 本身也是一個介面,其還包括以下操作:
- 按位置訪問:根據元素在序列中的位置索引訪問元素。
- 查詢:在序列中查詢指定的物件,並返回其位置索引。
- 迭代:擴充套件了 Iterator 介面,以利用序列的順序特性。
- List 子集合:在序列上執行任意範圍的操作。
public interface List<E> extends Collection<E>
ArrayList
ArrayList 簡介
ArrayList 基於陣列實現,是一個動態的陣列佇列。但是它和 Java 中的陣列又不一樣,它的容量可以自動增長。
ArrayList 繼承了 AbstractList,實現了 RandomAccess、Cloneable 和 Serializable 介面。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList 的容量為什麼會自動增長?
ArrayList 的列表物件實質上是儲存在一個引用型數組裡的,有人認為該陣列有“自動增長機制”可以自動改變 size 大小。準確地說,該陣列是無法改變大小的,實際上它只是改變了該引用型陣列的指向而已。下面,讓我們來看看 java 是怎樣實現 ArrayList 類的。
ArrayList 原始碼
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { //建立 ArrayList 完成之後,預設的容量為 0。 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // ArrayList 的擴容問題其實就是這個 Object 型別的陣列的擴容問題 //建立一個容量為 x 的 ArrayList 物件,其實就是一個長度為 x 的 Object 陣列 transient Object[] elementData; private void grow(int minCapacity) { // ArrayList的原始大小 int oldCapacity = elementData.length; // 在原始大小的基礎上計算擴充後的大小,擴充後的大小是元素大小的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //跟前面計算的擴充後長度 minCapacity 比較,取較大的那個為擴充後長度 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果擴充後長度大於最大長度 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 擴充 elementData = Arrays.copyOf(elementData, newCapacity); } }
ArrayList 實質
ArrayList 底層採用 Object 型別的陣列實現,當使用不帶引數的構造方法生成 ArrayList 物件時,實際上會在底層生成一個的 Object 型別陣列。首先,ArrayList 定義了一個私有的未被序列化的陣列 elementData ,用來儲存 ArrayList 的物件列表。
其次,以指定初始容量(Capacity)或把指定的 Collection 轉換為引用型陣列後例項化 elementData 陣列;如果沒有指定,則引用原始碼中的初始容量進行例項化。把私有陣列預先例項化,然後通過 copyOf 方法覆蓋原陣列,是實現自動改變 ArrayList 的大小(size)的關鍵。
add() 與 addAll() 的區別
add 是將傳入的引數作為當前 List 中的一個 Item 儲存,即使你傳入一個 List 也只會另當前的List增加1個元素。
addAll 是傳入一個 List,將此 List 中的所有元素加入到當前List中,也就是當前 List 會增加的元素個數為傳入的 List 的大小。
Collection demo1 = new ArrayList();
Collection demo2 = new ArrayList();
demo1.add(1);
demo1.add(2);
demo2.add("a");
demo2.add("b");
System.out.println("demo1:" + demo1);
System.out.println("demo1Size:" + demo1.size());
System.out.println("demo2:" + demo2);
System.out.println("demo2Size:" + demo2.size());
System.out.println();
demo1.add(demo2);
System.out.println("---------add--------");
System.out.println("demo1:" + demo1);
System.out.println("demo1Size:" + demo1.size());
System.out.println();
// 恢復原始的 demo1
demo1.remove(demo2);
demo1.addAll(demo2);
System.out.println("-------addAll------");
System.out.println("demo1:" + demo1);
System.out.println("demo1Size:" + demo1.size());
結果
demo1:[1, 2]
demo1Size:2
demo2:[a, b]
demo2Size:2
---------add--------
demo1:[1, 2, [a, b]]
demo1Size:3
-------addAll------
demo1:[1, 2, a, b]
demo1Size:4
建立幾個 ArrayList
Person p1 = new Person();
p1.setName("Springer");
p1.setAge(18);
p1.setSex("male");
Person p2 = new Person();
p2.setName("Simple");
p2.setAge(38);
p2.setSex("female");
List<Person> demo1 = new ArrayList<Person>();
demo1.add(p1);
demo1.add(p2);
ArrayList<Integer> argList = new ArrayList<Integer>();
argList.add(1);
argList.add(2);
argList.add(3);
argList.add(4);
argList.add(5);
argList.add(6);
四種遍歷方法:
1)通過迭代器遍歷
Iterator<Person> it = demo1.iterator();
while(it.hasNext()){
System.out.print(it.next());
System.out.println();
}
2)通過增強 for 迴圈遍歷
for(Person d : demo1){
System.out.println(d);
}
3)通過索引遍歷
Iterator<Person> it = demo1.iterator();
for(int i = 0; i < demo1.size(); i++){
System.out.print(demo1.get(i));
System.out.println();
}
-
Lambda 表示式
demo1.forEach(System.out::println);
System.out.println();
結果
name:Springer,age:18,sex:male
name:Simple,age:38,sex:female
ArrayList 中幾種常用的方法
1. 獲取 List 中元素的個數
List<Person> demo1 = new ArrayList<Person>();
System.out.println("---------獲取 List 中元素個數---------");
System.out.println(demo1.size());
---------獲取 List 中元素個數---------
2
2.在指定位置插入元素
- argList.add(index, element)
index:插入位置的下標
element:插入的元素
System.out.println("---------新增前遍歷---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
argList.add(2, 7);
System.out.println("---------新增後遍歷---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
---------新增前遍歷---------
1 2 3 4 5 6
---------新增後遍歷---------
1 2 7 3 4 5 6
3.刪除指定位置的元素
- argList.remove(index)
index:刪除元素下標
argList.remove(2);
System.out.println("---------刪除指定位置元素後遍歷---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
---------刪除指定位置元素後遍歷---------
1 2 3 4 5 6
4.刪除指定元素
- argList.remove(Object)
Object:刪除的元素
argList.remove((Object)5);
System.out.println("---------刪除指定元素後遍歷---------");
for(int i = 0; i < argList.size(); i++){
System.out.print(argList.get(i) + " ");
}
---------刪除指定元素後遍歷---------
1 2 3 4 6
5.判斷元素是否存在
- argList.contains(Object)
Object:需要判斷的元素
boolean obj3 = argList.contains(3);
boolean obj5 = argList.contains(5);
System.out.println("---------判斷是否存在元素---------");
System.out.println("ArrayList contains 3 is: " + obj3);
System.out.println("ArrayList contains 5 is: " + obj5);
---------判斷是否存在元素---------
ArrayList contains 3 is: true
ArrayList contains 5 is: false
6.清空集合
argList.clear()
System.out.println("---------清空前 argList 長度---------");
System.out.println("argList中元素個數:" + argList.size());
argList.clear();
System.out.println("---------清空後 argList 長度---------");
System.out.println("argList中元素個數:" + argList.size());
System.out.println("---------判斷 argList 是否為空---------");
System.out.println("判斷argList是否為空:" + argList.isEmpty());
---------清空前 argList 長度---------
argList中元素個數:5
---------清空後 argList 長度---------
argList中元素個數:0
---------判斷 argList 是否為空---------
判斷argList是否為空:true
LinkedList
LinkedList 簡介
LinkedList 是一個繼承於 AbstractSequentialList 的雙向連結串列。它也可以被當作堆疊、佇列或雙端佇列進行操作,其相對於 ArrayList 來說,是可以快速新增,刪除元素,ArrayList 新增刪除元素的話需移動陣列元素,可能還需要考慮到擴容陣列長度。
LinkedList 繼承了 AbstractSequentialList,實現了 List、Deque、Cloneable 和Serializable 介面
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList 屬性
LinkedList 本身的的屬性比較少,主要有三個,一個是 size,代表當前有多少個節點;一個是 first,代表第一個節點;一個是 last,代表最後一個節點。
建立一個 LinkedList
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("first");
linkedList.add("second");
linkedList.add("third");
System.out.println(linkedList);
[first, second, third]
LinkedList 中幾種常用的方法
1. 在 LinkedList 中第一個節點新增元素
- addFirst(element)
element:需要新增的元素
linkedList.addFirst("addFirst");
System.out.println(linkedList);
[addFirst, first, second, third]
2. 在 LinkedList 中最後一個節點新增元素
- addLast(element)
element:需要新增的元素
linkedList.addLast("addLast");
System.out.println(linkedList);
[addFirst, first, second, third, addLast]
3. 在 LinkedList 中指定位置插入元素
- add(index,element)
index:插入位置的下標
element:需要新增的元素
linkedList.add(2, "addByIndex");
System.out.println(linkedList);
[addFirst, first, addByIndex, second, third, addLast]
LinkedList 和 ArrayList 的區別
ArrayList 與 LinkedList 都是 List 介面的實現類,因此都實現了 List 的所有未實現的方法,只是實現的方式有所不同,而 List 介面繼承了 Collection 介面, Collection 介面又繼承了 Iterable 介面,因此可以看出 List 同時擁有了 Collection 與 Iterable 介面的特性。
ArrayList 實現了 List 介面,它是以陣列的方式來實現的,陣列的特性是可以使用索引的方式來快速定位物件的位置, 因此對於快速的隨機取得物件的需求,使用 ArrayList 實現執行效率上會比較好。
LinkedList 是採用連結串列的方式來實現 List 介面的,它本身有自己特定的方法,如: addFirst() , addLast() , getFirst(), removeFirst()等。 由於是採用連結串列實現的,因此在進行 insert 和 remove 動作時在效率上要比ArrayList要好得多!適合用來實現 Stack (堆疊)與 Queue (佇列),前者先進後出,後者是先進先出。
關於 ArrayList 和 LinkedList 的效率問題
ArrayList 底層是用陣列來儲存物件的,這種方式將物件放在連續的位置中。
優點:可以通過陣列下標快速的拿到值,大量修改時高效。
缺點:每一次新增和刪除都需要將操作的元素後面的元素們全部移動,非常麻煩。
LinkedList 則是將物件放在獨立的空間中,而且在每一個空間中存放下一個連結的索引。
優點:增加和刪除非常快速,其底層是連結串列,插入元素只要打斷這個節點插入元素即可。
缺點:要定位元素的位置時,要從頭一個一個尋找,耗費資源。
package test;
import java.util.ArrayList;
import java.util.LinkedList;
public class TimeForList {
public static long getArrayListFindTime(){
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 71000; i++) {
list.add(0,1);
}
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.get(i);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getLinkedListFindTime(){
LinkedList<Integer> list = new LinkedList<Integer>();
for (int i = 0; i < 71000; i++) {
list.add(0,1);
}
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.get(i);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getLinkedListAddTime(){
LinkedList<Integer> list = new LinkedList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(0,1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getArrayListAddTime(){
ArrayList<Integer> list = new ArrayList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(0, 1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getLinkedListAddLastTime(){
LinkedList<Integer> list = new LinkedList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(i,1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static long getArrayListAddLastTime(){
ArrayList<Integer> list = new ArrayList<Integer>();
long start = System.currentTimeMillis();
for (int i = 0; i < 71000; i++) {
list.add(i, 1);
}
long end = System.currentTimeMillis();
return end-start;
}
public static void main(String[] args) {
System.out.println("用LinkedList完成新增操作(開頭新增)所需時間為:" + getLinkedListAddTime() + "ms");
System.out.println( "用Linked完成新增操作(末尾新增)所需時間為:" + getLinkedListAddLastTime() + "ms");
System.out.println("用ArrayList完成新增操作(開頭新增)所需時間為:" + getArrayListAddTime() + "ms");
System.out.println( "用ArrayList完成新增操作(末尾新增)所需時間為:" + getArrayListAddLastTime() + "ms");
System.out.println("用LinkedList完成查詢操作所需時間為:" + getLinkedListFindTime() + "ms");
System.out.println("用ArrayList完成查詢操作所需時間為:" + getArrayListFindTime() + "ms");
}
}
結果
用 LinkedList 完成新增操作(開頭新增)所需時間為:15ms
用 Linked 完成新增操作(末尾新增)所需時間為:17ms
用 ArrayList 完成新增操作(開頭新增)所需時間為:941ms
用 ArrayList 完成新增操作(末尾新增)所需時間為:11ms
用 LinkedList 完成查詢操作所需時間為:4457ms
用 ArrayList 完成查詢操作所需時間為:2ms
由此可以直觀的看出 LinkedList 和 ArrayList 的效率
點選下載文章內容原始碼
提取密碼:jnfj
本文內容部分取自百度內容,如有雷同部分請見諒。