java基礎解析系列(十)---ArrayList和LinkedList源碼及使用分析

java基礎解析系列(十)---ArrayList和LinkedList源碼及使用分析

目錄

ArrayList

成員變量

  • 數組元素
111     private transient Object[] elementData;
  • 數組中元素的個數
118     private int size;

構造方法

  • 初始化容量爲10
Constructs an empty list with an initial capacity of ten.
137 
138     public ArrayList() {
139         this(10);
140     }
  • 能夠設置初始容量
127     public ArrayList(int initialCapacity) {
128         super();
129         if (initialCapacity < 0)
130             throw new IllegalArgumentException("Illegal Capacity: "+
131                                                initialCapacity);
132         this.elementData = new Object[initialCapacity];
133     }

ensureCapacity方法

178     public void ensureCapacity(int minCapacity) {
179         modCount++;
180         int oldCapacity = elementData.length;
181         if (minCapacity > oldCapacity) {
182             Object oldData[] = elementData;
183             int newCapacity = (oldCapacity * 3)/2 + 1;
184             if (newCapacity < minCapacity)
185                 newCapacity = minCapacity;
186             // minCapacity is usually close to size, so this is a win:
187             elementData = Arrays.copyOf(elementData, newCapacity);
188         }
189     }
  • 181行,判斷minCapacoty是否大於elementData數組的長度
  • 若是181結果爲true,183行設置新的容量爲舊的容量*3/2+1
  • 187行進行擴容,建立一個新容量的數組,而後將舊的數組元素複製到新數組中

順序add方法

377     public boolean add(E e) {
378         ensureCapacity(size + 1);  // Increments modCount!!
379         elementData[size++] = e;
380         return true;
381     }
  • 378行執行ensureCapacity方法,看插入一個元素後是否須要擴容
  • 379行,將待添加元素放置到下標size的位置,size+1

指定index的add方法

392     public void add(int index, E element) {
393         rangeCheckForAdd(index);
394 
395         ensureCapacity(size+1);  // Increments modCount!!
396         System.arraycopy(elementData, index, elementData, index + 1,
397                          size - index);
398         elementData[index] = element;
399         size++;
400     }
  • 393執行rangeCheckForAdd方法
577     private void rangeCheckForAdd(int index) {
578         if (index > size || index < 0)
579             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
580     }
  • rangeCheckForAdd方法判斷index是否超出範圍或者小於0
  • 395執行ensureCapacity方法看是否須要擴容
  • 396行將index開始的元素所有日後移動一位
  • 而後設置index位置的元素爲插入元素

remove方法

411     public E remove(int index) {
412         rangeCheck(index);
413 
414         modCount++;
415         E oldValue = elementData(index);
416 
417         int numMoved = size - index - 1;
418         if (numMoved > 0)
419             System.arraycopy(elementData, index+1, elementData, index,
420                              numMoved);
421         elementData[--size] = null; // Let gc do its work
422 
423         return oldValue;
424     }
  • 412行rangeCheck看傳入的index是否在範圍以內
569     private void rangeCheck(int index) {
570         if (index >= size)
571             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
572     }
  • 415經過下標得到該元素
  • 419行進行數組的移動,elementData數組中位置在 index+1 到 index+numMoved-1 之間的組件被分別複製到elementData數組中的 index 到 index+numMoved-1 位置。
  • 把index後面的元素向前移動一位後,將size-1的位置設置爲null,方便gc

get方法

348     public E get(int index) {
349         rangeCheck(index);
350 
351         return elementData(index);
352     }
  • 349行查找index是否超出範圍
  • 351直接經過座標從數組中返回

LinkedList

成員變量

95     private transient Entry<E> header = new Entry<E>(null, null, null);
96     private transient int size = 0;
  • 95行head爲一個頭結點
  • size爲鏈表大小

構造方法

101    public LinkedList() {
102        header.next = header.previous = header;
103    }
  • 102行將header的前節點和後節點設置爲header自己,從這裏也能夠看出這是一個雙向鏈表

add方法

214    public boolean add(E e) {
215        addBefore(e, header);
216        return true;
217    }
  • 215行執行addBefore方法
794    private Entry<E> addBefore(E e, Entry<E> entry) {
795        Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
796        newEntry.previous.next = newEntry;
797        newEntry.next.previous = newEntry;
798        size++;
799        modCount++;
800        return newEntry;
801    }
  • 795行建立一個新的節點
  • 796行和797行修改節點的指針,將新節點放入鏈表

get方法

331    public E get(int index) {
332        return entry(index).element;
333    }
  • 執行entry方法
380    private Entry<E> entry(int index) {
381        if (index < 0 || index >= size)
382            throw new IndexOutOfBoundsException("Index: "+index+
383                                                ", Size: "+size);
384        Entry<E> e = header;
385        if (index < (size >> 1)) {
386            for (int i = 0; i <= index; i++)
387                e = e.next;
388        } else {
389            for (int i = size; i > index; i--)
390                e = e.previous;
391        }
392        return e;
393    }
  • 從這個方法能夠看出並非就是從前日後一個一個尋找,而是先將size>>1也就是將size除以2,看此時要查看的下標是小於仍是大於這個數,若是小於這個數,說明位於鏈表中點的前面,用next指針尋找,正向尋找,反之用previous指針來尋找,這樣能夠減小遍歷的次數
  • 不過不能像ArrayList經過index直接定位,仍是要一個一個尋找

remove方法

232    public boolean remove(Object o) {
233        if (o==null) {
234            for (Entry<E> e = header.next; e != header; e = e.next) {
235                if (e.element==null) {
236                    remove(e);
237                    return true;
238                }
239            }
240        } else {
241            for (Entry<E> e = header.next; e != header; e = e.next) {
242                if (o.equals(e.element)) {
243                    remove(e);
244                    return true;
245                }
246            }
247        }
248        return false;
249    }
  • 能夠看到,從前日後遍歷,找到Object o後執行remove方法
803    private E remove(Entry<E> e) {
804        if (e == header)
805            throw new NoSuchElementException();
806
807        E result = e.element;
808        e.previous.next = e.next;
809        e.next.previous = e.previous;
810        e.next = e.previous = null;
811        e.element = null;
812        size--;
813        modCount++;
814        return result;
815    }
  • 刪除這個節點後,從新調整鏈表

總結與對比

  • 效率方面,這兩個集合對應數據結構中的兩個線性表,一個數組一個鏈表,數組能夠經過下標能夠快速定位元素,因此天然查找和修改效率高。鏈表不能快速定位元素,只能一個一個找,因此天然查找效率沒有數組快,而鏈表的優點在於他能快速插入快速刪除由於只需修改一下節點的指針就能夠,不須要像ArrayList移動元素。
  • 容量方面,由於數組是有容量的,因此當容量不足的時候,須要擴容,擴容後就覺得者須要進行一次複製,因此若是使用ArrayList的時候,要初始化一個合適的容量,避免擴容的開銷。而鏈表就沒有大小限制,插入一個元素,只要插入一個節點就好了
  • 編程世界裏面,同一個問題會有不少的方案,有優勢也會必定會有缺點,只是在哪一種場景下,優勢大於缺點罷了

我以爲分享是一種精神,分享是個人樂趣所在,不是說我以爲我講得必定是對的,我講得可能不少是不對的,可是我但願我講的東西是我人生的體驗和思考,是給不少人反思,也許給你一秒鐘、半秒鐘,哪怕說一句話有點道理,引起本身心裏的感觸,這就是我最大的價值。(這是我喜歡的一句話,也是我寫博客的初衷)

做者:jiajun 出處: http://www.cnblogs.com/-new/
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。若是以爲還有幫助的話,能夠點一下右下角的【推薦】,但願可以持續的爲你們帶來好的技術文章!想跟我一塊兒進步麼?那就【關注】我吧。html

相關文章
相關標籤/搜索