1. 程式人生 > >ArrayList和LinkedList的簡單實現

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學習筆記--類ArrayListLinkedList實現

java 集合 list 在集合Collection下的List中有兩個實現使用的很頻繁,一個是ArrayList,另一個是LinkedList,在學習中肯定都會有這樣的疑問:什麽時候適合使用ArrayList,什麽時候用LinkedList?這時,我們就需要了解ArrayList和Lin

Java基礎面試題4-描述一下ArrayListLinkedList各自實現區別

   1.List是介面類,ArrayList和LinkedList是List的實現類。    2.ArrayList是動態陣列(順序表)的資料結構。順序表的儲存地址是連續的,所以在查詢比較快,但是在插入和刪除時,由於需要把其它的元素順序向後移動(或向前移動),所以比較熬時。

ArrayListLinkedList各自實現區別

ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構。 對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移

ArrayListLinkedList底層實現原理

1.說一下 ArrayList 底層實現方式? ①ArrayList 通過陣列實現,一旦我們例項化 ArrayList 無引數建構函式預設為陣列初始化長度為 10②add 方法底層實現如果增加的元素個數超過了 10 個,那麼 ArrayList 底層會新生成一個數組,長度為

ArrayListlinkedList底層實現原理以及區別?

ArrayList 先說說Arraylist,Arraylist是基於動態陣列實現的,所以查詢速度快,但是增刪操作的速度會比較慢,但是為什麼會這樣?我解釋一下動態陣列,基本就可以明白這個問題了。 先說說靜態陣列是怎麼來儲存資料的,當我們使用new來建立一個數組,實際上是在

描述一下ArrayListLinkedList各自實現區別

ArrayList,LinkedList,Vestor這三個類都實現了java.util.List介面,但它們有各自不同的特性,主要如下: 一、同步性        ArrayList,LinkedList是不同步的,而Vestor是同步的。所以如果不要求執行緒安全的話,

[原始碼分析]ArrayListLinkedList如何實現的?我看你還有機會!

> 文章已經收錄在 [Github.com/niumoo/JavaNotes](https://github.com/niumoo/JavaNotes) ,更有 Java 程式設計師所需要掌握的核心知識,歡迎Star和指教。 > 歡迎關注我的[公眾號](https://github.com/n

ArrayListLinkedList簡單實現

ArrayList提供了一宗可增長陣列的實現。有點事對get和set呼叫花費常數時間。缺點是插入和刪除代價昂貴,除非插入和刪除是在ArrayList的末端進行。 LinkedList提供了雙鏈表實現。優點是,插入和刪除開銷很小,花費常數時間。缺點是不容易做索引

java集合的實現細節--ArrayListLinkedList

方法 封裝 對象 通過 場景 部分 index索引 ava 本質   ArrayList和LinkedList的實現差異   List代表一種線性表的數據結構,ArrayList則是一種順序存儲的線性表,ArrayList底層采用動態數組的形式保存每一個集合元素,Link

arraylistlinkedlist內部的實現大致是怎樣的

這樣的 刪除元素 man 是否 工作 rim 刪除數據 value 基礎 1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。 2.對於隨機訪問get和set,ArrayList優於LinkedList,因為ArrayList可以

ArrayList LinkedList Vector使用上有什麼區別?實現上有什麼區別?

ArrayList 和 Vector 使用上有什麼區別?實現上有什麼區別? Vector和ArrayList在使用上非常相似,都可用來表示一組數量可變的物件應用 的集合,並且可以隨機地訪問其中的元素。 1 Vector的方法都是同步的(Synchroni

ArrayListLinkedList中的get方法底層實現

1.ArrayList的get方法  因為底層是陣列 直接通過下標獲得 2.LinkedList的get方法  因為底層是連結串列,連結串列沒有下標,需要迭代遍歷: if (index < (size >> 1)) { Nod

(一)ArrayListLinkedList的原理、Java程式碼實現、效能比較

一、ArrayList1.1、陣列和集合的區別    動態大小,即陣列的大小不可變,集合的大小可變。    ArrayList從名字上來講是陣列列表,表面上是動態大小,其底層實現原理其實還是一個數組。1.2、簡單模擬ArrayList    模擬過程中要注意Array和Arr

List動態陣列中兩個實現類:ArrayListLinkedList的用法區別

List 簡介 List是一個動態陣列,使用者可以對插入元素的位置進行精確的控制,可以根據整數索引(index)獲取序列中的元素,允許插入重複的值,包null值 常用的兩個實現類:ArrayList、LinkedList Arra

四、ArrayListLinkedList內部的實現大致是怎樣的?

 1、ArrayList集合      ArrayList是List介面的一個實現類,它是程式中最常見的一種集合。在ArrayList內部封裝了一個長度可變的陣列物件,當存入的元素超過陣列長度時,ArrayList會在記憶體分配一個更大的陣列來儲存這些元素,因此Array

[從今天開始修煉資料結構]線性表及其實現以及實現有Itertor的ArrayListLinkedList

一、線性表   1,什麼是線性表   線性表就是零個或多個數據元素的有限序列。線性表中的每個元素只能有零個或一個前驅元素,零個或一個後繼元素。在較複雜的線性表中,一個數據元素可以由若干個資料項組成。比如牽手排隊的小朋友,可以有學號、姓名、性別、出生日期等資料項。   2,線性表的抽象資料型別   線性表的抽象

java中ArrayListLinkedList區別

插入 list 新的 查找 arr tro 基於 列表 時間復雜度 ArrayList和LinkedList最主要的區別是基於不同數據結構 ArrayList是基於動態數組的數據結構,LinkedList基於鏈表的數據結構,針對這點,從時間復雜度和空間復雜度來看主要區別:

ArrayListLinkedList的區別

內部 str sta family 足夠 我們 素數 private 不存在   ArrayList和Vector使用了數組的實現,可以認為ArrayList或者Vector封裝了對內部數組的操作,比如向數組中添加,刪除,插入新的元素或者數據的擴展和重定向。   Linke

Java中arraylistlinkedlist源代碼分析與性能比較

rom fin java 獲取 color () serializa padding previous Java中arraylist和linkedlist源代碼分析與性能比較 1,簡單介紹 在java開發中比較經常使用的數據結構是arra

從源代碼來理解ArrayListLinkedList差別

表示 得到 代碼 -a art 一個 http 指定 lin 從源代碼理解ArrayList和LinkedList差別 ArrayList ArrayList默認容量為10,實質是一個數組用於存放元素,size表示ArrayList所包括的元素個數。