數據結構和算法二(ArrayList和LinkedList源碼解析)

ArrayList源碼解析

  • ArrayList 是基于數組的方式來實現數據的增加、刪除、修改、查找
    1、內部維護了兩個變量

    private transient Object[] elementData ;該數組緩存的集合中的元素,集合的容量就是該數組的長度,
        elementData 是 transient修飾,說明在序列化時 數組 elementData不在序列化范圍內。
    private int size;  集合的大小,集合中元素的數量
    

    2、 在看看 ArrayList的構造器

      構造一個指定容量的 initialCapacity 的空的集合 super調用的 AbstractList的默認構造方法,
      該方法是一個空的方法 然后判斷傳入的參數不能小于 0否則就跑出非法參數異常, 然后直接創建了一個 長度為 initialCapacity的數組對象,并將該對象賦值給當前實例的 elementData屬性,用以存放集合中元素 
      public ArrayList(int initialCapacity){
        super();
          if(initialCapacity<0){
                throw new IllegalArgumentException("Illegal Capacity initialCapacity")
          }
         this.elementData=new Object[initialCapacity];
        }
    
      public ArrayList(){ this{10}}
      構造一個默認為 10的空的集合
    
      再來看一個包含指定集合  中的所有元素的集合,該集合中元素的順序是以  的迭代器返回元素的順序
      public ArrayList(Collection<? extend E> c)
      { 
          elementData=c.toArray(); 
          size=elementData.length; 
          if(elementData.getClass!=Object[].class) {                  
             elementData=Arrays.copyof(elementData,size,Object[].class);
          }
       }
    

    3、從上面的源碼中 看到是先將 c.toArray()方法返回賦值給 elementData,將大小賦值給 size,然后進行判斷 如果為 true,調用 Arras.copy方法創建一個新的 Object[]新數組,將 elementData 中元素 copy 到新 Object
    數組中 然后賦值給 elementData()

    4、add()方法

    插入對象 首先將 size+1,纏上一個 minCapacity的變量,調用 
      ensureCapacity( minCapacity)
    方法保證數組插入一個元素時有可用的容量,然后將元素 放到 elementData的 size位置,size+1
    public boolean add(E e){ 
        ensureCapacity(size+1); 
        elementData[size++]=e;
        return true;
    }
    
    public void ensureCapacity(int minCapacity){ 
        modCount++;
        int oldCapacity=elementData.length; 
        if(minCapacity>oldCapacity){
            Object[] oldData=elementData;
            int newCapacity=(oldCapacity*3)/2+1; 
        if(newCapacity<minCapacity)
             newCapacity=minCapacity;
         elementData=Arrays.copy(elementData,newCapacity);
        }
    }
    
    
    必要時數組的擴容,首先將 
    modCoun+1 modeCount
    表示修改數組結構次數,如果傳入的 minCapacity大于目前 elementData的容量則將容量擴展到     (oldCapacity*3)/2+1若此時容量仍然小于 minCapacity,直接將 minCapacity設置為最新的容量,
    最后使用 Arrays的 copy方法到新的數組中 并將新數組賦值給 elementData.
    
    在指定下表 index處插入 element首先判斷下標 index 的參數是否有效,否則跑出異常,然后調用
    ensureCapacity(size+1)要確保有足夠容量來插入數據,然后調用 System.arraycopy 從 index 開始,將 
    elementData 數組元素 copy到從 index+1 開始的地方,copy 的長度為 size-index,這樣 index下標出的位置就空出來了,然后將元素 element 放到下標為 index處的位置,最后將 size++ 
    public void add(int index,E element){
        if(index>size || index<0){
          throw new IndexOutOfBoundsException("Index"+index+",Size:"+size);
        }
        ensureCapacity(size+1);
        這一步是擴容 
      Systim.arraycopy(elementData,index,elementData,index+1,size-index);
      elementData[index]=element;
      size++;
    }
    

    5、刪除方法

    刪除指定元素,分為兩種情況, 若指定的元素為 null,則遍歷當前的 elementData 數組,如果某一個下標 index的值為 null,則調用方法 fastRemove 刪除元素, 如果指定的不為 null,則遍歷 若某一個 index
    下標處的元素和 o 進行 equals則快速刪除元素 但是只能刪除第一個出現的元素 快速刪除是 將 index后面的元素往前復制一位 ,這樣就達到刪除的目的
    
image.png

6、查找方法

   查找非常簡單,(這里不粘代碼了 在下面和LinkedList對比起來看)
  • 注意

     ArrayList是基于數組的方式實現的, 所以查找效率高,但是每次插入或者刪除元素,都要大
    量的移動元素,所以效率很低, 這里面主要涉及幾個重要的方法 一個是減小數組的容量 就是講當前數組的容量縮小為實際存儲的容量trimToSize,還有一個就是擴容,如果容量不夠,就設置新的容量為舊的容量的
    1.5倍加1,因此建議能夠事先確定元素數量的情況下用ArrayList(數組都是這樣的特征)
    

LinkedList源碼解析

  • LinkedList** 繼承結構開始分析

    public class LinkedList<E> extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    LinkedList是 一個繼承于 AbstractSequentialList的雙向鏈表。它也可以被當作堆棧、隊列或雙端隊列
    列進 行操作。
    LinkedList實現 List接 口,能對它進行隊列操作。
    LinkedList實現 Deque接 口,雙向隊列它 支持從兩個端點 方向檢索和插 入元素。
    LinkedList實現了了Cloneable接 口,即覆蓋了了函數 clone(),能克隆
    LinkedList實現 java.io.Serializable 接 口,這意味著 LinkedList支持序列化,能通過序列化去傳輸。

  • LinkedList屬性
    關鍵字 transient 標識的字段的 生命周期僅存于調 用者的內存中 而不不會寫到磁盤 里里持久化

     transient int size = 0;
      /**
        *Pointer to first node.
        *Invariant: (first == null && last == null) ||
        *           (first.prev == null && first.item != null)
      */
      transient Node<E> first;
      transient Node<E> last;
    
      LinkedList每個存儲元素是 一個 Node對象
    
    private static class Node<E> {
    
      Node<E> next; Node<E> prev;
      Node(Node<E> prev, E element, Node<E> next) { 
           this.item = element;
           this.next = next;
           this.prev = prev;
      }
    }
    在這個對象可以分析出LinkedList存儲開銷比 Arraylist大因為 LinkedList每個元素在內存里里多分配了了Node next; , Node prev;兩個變量值得出ArrayList 和 
    LinkedList 存儲相同 大 小的數據LinkedList占內存 比較 大
    
  • LinkedList 構造方法

     public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
     }
    
    return addAll(size, c);開始分析.參數 1.添加位置 , 參數 2,需要添加的數據;
    
     public boolean addAll(int index, Collection<? extends E> c) { 
         checkPositionIndex(index);
         Object[] a = c.toArray();
         int numNew = a.length;
         if (numNew == 0)
           return false;
       上部分校驗數據是否為0但是少了了空指針校驗
    
       Node<E> pred, succ;
       if (index == size) {
         保存index處的節點。插 入位置如果是size,則在頭結點前 面插 入,否則在獲取
         index處的節點插 入succ = null;
         pred = last;
       } else {
         獲取前 一個節點,插 入時需要修改這個節點的next引 用
         succ = node(index);
         pred = succ.prev;
       }
       可以理理解node擺放的位置
       for (Object o : a) {
       在強制類型轉換的時候編譯器 會給出警告@SuppressWarnings("unchecked")此注解去除警告
       @SuppressWarnings("unchecked") E e = (E) o;
       創建node參數1.node頭參數2.數據參數3.node尾
       Node<E> newNode = new Node<>(pred, e, null);
       if (pred == null)
         first = newNode;
       else
        修改前 一節點的next指針
       pred.next = newNode;
       pred = newNode;
     }
       收尾的處理理
       if (succ == null) {
         last = pred;
       } else {
         pred.next = succ;
         succ.prev = pred;
       }
     size += numNew;
     modCount++;
     return true;
     }
    

LinkedList初始化操作 會處理理一 大坨的頭尾地址轉換ArrayList初始化操作 會不不斷的 copy數組為了了
Arrays.copyOf(elementData, size, Object[].class);

(說明 這些東西原來在world上寫的 復制過來格式全部變了 所以我就截圖了)

  • LinkedList 添加元素


    image.png

    linkLast方法

    image.png

    linkBefore方法(按指定位置插)

    image.png

    LinkedList 添加 又是 處理理 頭--尾.地址轉換
    ArrayList 添加 又會 copy 數組.為了了 自增


    image.png
  • LinkedList 刪除元素


    image.png

    unlink方法
    刪除節點 把節點的.所有屬性指 null


    image.png

    LinkedList 刪除元素.先把 自 己的東 西給別 人.然后 自 己指空.
    ArrayList 刪除元素.還是操作 Copy 數組

    image.png
  • LinkedList 獲取元素

    image.png

    node方法

    image.png

    LinkedList 獲取元素.需要 用循環找
    ArrayList 獲取元素 直接 index 下標查找

    image.png
  • 上面的源碼分析是對上一篇的線性表的一個擴展,因為ArrayList和LinkedList就是用的順序存儲結構和鏈式存儲結構的,好了 先到這里 大家有什么疑問或者哪里寫的不對 可以指正。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容