ArrayList和LinkedList的簡單實現
- ArrayList提供了一宗可增長陣列的實現。有點事對get和set呼叫花費常數時間。缺點是插入和刪除代價昂貴,除非插入和刪除是在ArrayList的末端進行。
- LinkedList提供了雙鏈表實現。優點是,插入和刪除開銷很小,花費常數時間。缺點是不容易做索引,get和set呼叫昂貴,除非呼叫接近表的斷點的項(離哪端近就從哪端開始)。
這兩個集合主要是內部對資料的儲存方式不一樣,一個用的陣列,一個用的連結串列。
陣列和連結串列
關於數組合連結串列在之前有個簡單的比較
簡單陣列
array list
- 陣列可以使遍歷以線性時間執行
O(N)
。 - 查詢是常數時間
O(1)
。 - 不過插入和刪除開銷昂貴,如果發生在第一個元素上,時間是
O(N)
O(1)
當插入和刪除都只對高階操作,陣列也比較合適,否則就應用 連結串列 linked list
簡單鏈表
使用連結串列是因為連結串列在記憶體中是不連續的,不用因為一個元素的插入或刪除而引起其他元素的變動。
連結串列由一系列節點組成,節點不需要在記憶體中相連,因此每一個節點除了自身元素外還要包含自己的後繼元的節點的鏈(link),稱之為next鏈
。最後一個節點的next鏈引用null。
- 遍歷的時間是
O(N)
,跟陣列一樣。 - 查詢的時間,因為需要從第一個節點開始查詢,所以時間也是線性的,
O(N)
。要查詢第x個元素,花費的時間是O(x), 效率不如陣列。 - 刪除可以通過修改next鏈的引用來實現
- 插入也一樣,獲取一個新節點,修改兩個next鏈的引用,
刪除A2:
在A1後插入Ax:
雙鏈表
讓每一個節點擁有其前驅節點的引用就成了雙鏈表。
ArrayList和LinkedList
- ArrayList提供了一宗可增長陣列的實現。有點事對get和set呼叫花費常數時間。缺點是插入和刪除代價昂貴,除非插入和刪除是在ArrayList的末端進行。
- LinkedList提供了雙鏈表實現。優點是,插入和刪除開銷很小,花費常數時間。缺點是不容易做索引,get和set呼叫昂貴,除非呼叫接近表的斷點的項(離哪端近就從哪端開始)。
ArrayList的實現
- 內部使用陣列來儲存資料,有一個預設的陣列容量
- 當容量不夠的時候,擴容一個新陣列,將老資料複製到新陣列,釋放舊陣列。
- 內部類實現 MyIterator
- 增強for迴圈的實現 implements Iterable
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
/**
* @author sll on 2017/6/30.
*/
public class MyArrayList<Element> implements Iterable<Element> {
private static final int DEFAULT_CAPACITY = 5;
private int size;
private Element[] elements;
private int modCount = 0;
public MyArrayList() {
elements = (Element[]) new Object[DEFAULT_CAPACITY];
clear();
}
public void clear() {
size = 0;
ensureCapacity(DEFAULT_CAPACITY);
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void trimToSize() {
ensureCapacity(size);
}
//這裡是查詢,使用陣列直接就查出來了,所以一次查詢的時間是 O(1)
public Element get(int index) {
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException();
}
return elements[index];
}
public Element set(int index, Element newElement) {
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException();
}
Element old = elements[index];
elements[index] = newElement;
return old;
}
public boolean add(Element e) {
return add(size, e);
}
//這裡的增加和刪除就要移動很多項,所以一次插入或刪除的速度是 O(N)
public boolean add(int index, Element e) {
if (elements.length == size) {
ensureCapacity(size * 2 + 1);
}
System.arraycopy(elements, index, elements, index + 1, size - index);
elements[index] = e;
size++;
modCount++;
return true;
}
public Element remove(int index) {
Element removeElement = elements[index];
int moveNum = size - index - 1;
if (moveNum > 0) {
System.arraycopy(elements, index + 1, elements, index, moveNum);
}
size--;
elements[size] = null;
modCount++;
return removeElement;
}
public void ensureCapacity(int newCapacity) {
if (newCapacity < size)
return;
elements = Arrays.copyOf(elements, newCapacity);
}
public Itr iterator() {
return new Itr();
}
private class Itr implements Iterator<Element> {
private int exceptMocCount = modCount;
private int current = 0;
@Override
public boolean hasNext() {
return current < size();
}
@Override
public Element next() {
if (exceptMocCount != modCount) {
throw new ConcurrentModificationException();
}
if (!hasNext()) {
throw new NoSuchElementException();
}
return elements[current++];
}
@Override
public void remove() {
if (exceptMocCount != modCount) {
throw new ConcurrentModificationException();
}
MyArrayList.this.remove(--current);
}
@Override
public void forEachRemaining(Consumer<? super Element> action) {
}
}
}
LinkedList的實現
- 使用雙鏈表來實現
- 節點類Node,為私有靜態內部類(巢狀類),包含資料和前後節點的鏈
- 有兩個額外的節點,頭節點和尾節點,中間才是資料
- 內部實現MyIterator
- 增強for迴圈的實現 implements Iterable
- 對add和remove增加一個計數modCount,在迭代器的hasNext和next中比較這個計數,不同就說明這個集合被修改了,就丟擲異常ConcurrentModificationException。
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* @author sll on 2017/6/30.
*/
public class MyLinkedList<E> implements Iterable<E> {
private int theSize = 0;
private int modCount = 0;
private Node<E> startNode, endNode;//額外的頭節點和尾節點
public MyLinkedList() {
clear();
}
private void clear() {
startNode = new Node(null, null, null);
endNode = new Node(null, startNode, null);
startNode.next = endNode;
theSize = 0;
modCount++;
}
public int size() {
return theSize;
}
public boolean isEmpty() {
return size() == 0;
}
public boolean add(E x) {
return add(size(), x);
}
public boolean add(int index, E x) {
addBefore(getNode(index), x);
return true;
}
public E get(int index) {
return getNode(index).data;
}
public E set(int index, E x) {
Node<E> p = getNode(index);
E old = p.data;
p.data = x;
return old;
}
public E remove(int index) {
return remove(getNode(index));
}
private void addBefore(Node<E> p, E x) {
Node<E> newNode = new Node<>(x, p.prev, p);
newNode.prev.next = newNode;
p.prev = newNode;
theSize++;
modCount++;
}
private Node<E> getNode(int index) {
Node<E> p;
if (index < 0 || index > size())
throw new IndexOutOfBoundsException();
if (index < size() / 2) {
p = startNode.next;
for (int i = 0; i < index; i++) {
p = p.next;
}
} else {
p = endNode;
for (int i = size(); i > index; i--) {
p = p.prev;
}
}
return p;
}
private E remove(Node<E> x) {
x.prev.next = x.next;
x.next.prev = x.prev;
theSize--;
modCount++;
return x.data;
}
@Override
public Iterator<E> iterator() {
return new MyIterator();
}
private static class Node<E> {
public E data;
public Node<E> prev;
public Node<E> next;
public Node(E data, Node<E> prev, Node<E> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
private class MyIterator implements Iterator<E> {
private Node<E> current = startNode.next;
private int expectedModCount = modCount;
private boolean okToRemove = false;
@Override
public boolean hasNext() {
return current!= endNode;
}
@Override
public E next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (!hasNext()) {
throw new NoSuchElementException();
}
E nextItem = current.data;
current = current.next;
okToRemove = true;
return nextItem;
}
@Override
public void remove() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (!okToRemove) {
throw new NoSuchElementException();
}
MyLinkedList.this.remove(current.prev);
okToRemove = false;
expectedModCount++;
}
}
}
時間對比
以分別用ArrayList和LinkedList用下面三個方法做一個時間對比:
//從末端新增,建立一個由N個項的List。
public static void makeList1(List<Integer> list, int N) {
list.clear();
for (int i = 0; i < N; i++) {
list.add(i);
}
}
//從首端新增,建立一個由N個項的List。
public static void makeList2(List<Integer> list, int N) {
list.clear();
for (int i = 0; i < N; i++) {
list.add(0, i);
}
}
//獲取長度為N的List的每一個值求和。
public static void makeList3(List<Integer> list) {
long total = 0;
for (int i = 0; i < list.size(); i++) {
total += list.get(i);
}
}
首先建立兩個集合,N=100000,列印耗費時間(ms):
List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<Integer>();
makeList1
makeList1(arrayList, 100000); makeList1(linkedList, 100000);
結果可能會不一樣,但也是相近的。
執行時間都是O(N)
makeList2
makeList2(arrayList, 100000); makeList2(linkedList, 100000);
linkedList花費的時間少得多,執行時間是O(N)
而ArrayList因為每次在頭部插入都需要將之前所有項向後移動一位,所以執行時間是O(N^2)
makeList3
makeList2(arrayList, 100000); makeList2(linkedList, 100000); makeList3(arrayList); makeList3(linkedList);
這個每次都要查詢,所以ArrayList比較快,執行時間是O(N)
而inkedList的執行時間是O(N^2)
用迭代器實現makeList3
將makeList3改成:
public static void makeList3(List<Integer> list) { long total = 0; for (Integer i : list) { total+=i; } }
兩個都很相近了,都是O(N)。
參考《資料結構與演算法分析java版》
相關推薦
java學習筆記--類ArrayList和LinkedList的實現
java 集合 list 在集合Collection下的List中有兩個實現使用的很頻繁,一個是ArrayList,另一個是LinkedList,在學習中肯定都會有這樣的疑問:什麽時候適合使用ArrayList,什麽時候用LinkedList?這時,我們就需要了解ArrayList和Lin
Java基礎面試題4-描述一下ArrayList和LinkedList各自實現和區別
1.List是介面類,ArrayList和LinkedList是List的實現類。 2.ArrayList是動態陣列(順序表)的資料結構。順序表的儲存地址是連續的,所以在查詢比較快,但是在插入和刪除時,由於需要把其它的元素順序向後移動(或向前移動),所以比較熬時。
ArrayList和LinkedList各自實現和區別
ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構。 對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移
ArrayList和LinkedList底層實現原理
1.說一下 ArrayList 底層實現方式? ①ArrayList 通過陣列實現,一旦我們例項化 ArrayList 無引數建構函式預設為陣列初始化長度為 10②add 方法底層實現如果增加的元素個數超過了 10 個,那麼 ArrayList 底層會新生成一個數組,長度為
ArrayList和linkedList底層實現原理以及區別?
ArrayList 先說說Arraylist,Arraylist是基於動態陣列實現的,所以查詢速度快,但是增刪操作的速度會比較慢,但是為什麼會這樣?我解釋一下動態陣列,基本就可以明白這個問題了。 先說說靜態陣列是怎麼來儲存資料的,當我們使用new來建立一個數組,實際上是在
描述一下ArrayList和LinkedList各自實現和區別
ArrayList,LinkedList,Vestor這三個類都實現了java.util.List介面,但它們有各自不同的特性,主要如下: 一、同步性 ArrayList,LinkedList是不同步的,而Vestor是同步的。所以如果不要求執行緒安全的話,
[原始碼分析]ArrayList和LinkedList如何實現的?我看你還有機會!
> 文章已經收錄在 [Github.com/niumoo/JavaNotes](https://github.com/niumoo/JavaNotes) ,更有 Java 程式設計師所需要掌握的核心知識,歡迎Star和指教。 > 歡迎關注我的[公眾號](https://github.com/n
ArrayList和LinkedList的簡單實現
ArrayList提供了一宗可增長陣列的實現。有點事對get和set呼叫花費常數時間。缺點是插入和刪除代價昂貴,除非插入和刪除是在ArrayList的末端進行。 LinkedList提供了雙鏈表實現。優點是,插入和刪除開銷很小,花費常數時間。缺點是不容易做索引
java集合的實現細節--ArrayList和LinkedList
方法 封裝 對象 通過 場景 部分 index索引 ava 本質 ArrayList和LinkedList的實現差異 List代表一種線性表的數據結構,ArrayList則是一種順序存儲的線性表,ArrayList底層采用動態數組的形式保存每一個集合元素,Link
arraylist和linkedlist內部的實現大致是怎樣的
這樣的 刪除元素 man 是否 工作 rim 刪除數據 value 基礎 1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。 2.對於隨機訪問get和set,ArrayList優於LinkedList,因為ArrayList可以
ArrayList 和 LinkedList 和Vector使用上有什麼區別?實現上有什麼區別?
ArrayList 和 Vector 使用上有什麼區別?實現上有什麼區別? Vector和ArrayList在使用上非常相似,都可用來表示一組數量可變的物件應用 的集合,並且可以隨機地訪問其中的元素。 1 Vector的方法都是同步的(Synchroni
ArrayList和LinkedList中的get方法底層實現
1.ArrayList的get方法 因為底層是陣列 直接通過下標獲得 2.LinkedList的get方法 因為底層是連結串列,連結串列沒有下標,需要迭代遍歷: if (index < (size >> 1)) { Nod
(一)ArrayList和LinkedList的原理、Java程式碼實現、效能比較
一、ArrayList1.1、陣列和集合的區別 動態大小,即陣列的大小不可變,集合的大小可變。 ArrayList從名字上來講是陣列列表,表面上是動態大小,其底層實現原理其實還是一個數組。1.2、簡單模擬ArrayList 模擬過程中要注意Array和Arr
List動態陣列中兩個實現類:ArrayList和LinkedList的用法和區別
List 簡介 List是一個動態陣列,使用者可以對插入元素的位置進行精確的控制,可以根據整數索引(index)獲取序列中的元素,允許插入重複的值,包null值 常用的兩個實現類:ArrayList、LinkedList Arra
四、ArrayList和LinkedList內部的實現大致是怎樣的?
1、ArrayList集合 ArrayList是List介面的一個實現類,它是程式中最常見的一種集合。在ArrayList內部封裝了一個長度可變的陣列物件,當存入的元素超過陣列長度時,ArrayList會在記憶體分配一個更大的陣列來儲存這些元素,因此Array
[從今天開始修煉資料結構]線性表及其實現以及實現有Itertor的ArrayList和LinkedList
一、線性表 1,什麼是線性表 線性表就是零個或多個數據元素的有限序列。線性表中的每個元素只能有零個或一個前驅元素,零個或一個後繼元素。在較複雜的線性表中,一個數據元素可以由若干個資料項組成。比如牽手排隊的小朋友,可以有學號、姓名、性別、出生日期等資料項。 2,線性表的抽象資料型別 線性表的抽象
java中ArrayList和LinkedList區別
插入 list 新的 查找 arr tro 基於 列表 時間復雜度 ArrayList和LinkedList最主要的區別是基於不同數據結構 ArrayList是基於動態數組的數據結構,LinkedList基於鏈表的數據結構,針對這點,從時間復雜度和空間復雜度來看主要區別:
ArrayList和LinkedList的區別
內部 str sta family 足夠 我們 素數 private 不存在 ArrayList和Vector使用了數組的實現,可以認為ArrayList或者Vector封裝了對內部數組的操作,比如向數組中添加,刪除,插入新的元素或者數據的擴展和重定向。 Linke
Java中arraylist和linkedlist源代碼分析與性能比較
rom fin java 獲取 color () serializa padding previous Java中arraylist和linkedlist源代碼分析與性能比較 1,簡單介紹 在java開發中比較經常使用的數據結構是arra
從源代碼來理解ArrayList和LinkedList差別
表示 得到 代碼 -a art 一個 http 指定 lin 從源代碼理解ArrayList和LinkedList差別 ArrayList ArrayList默認容量為10,實質是一個數組用於存放元素,size表示ArrayList所包括的元素個數。