1. 程式人生 > >java集合框架使用原理分析

java集合框架使用原理分析

      集合是我們日常程式設計中可能用的很多的技術之一 使用頻率極高 可能平時就會知道怎麼去用 但是集合之間的關係與不同之處都不是很清楚 對它們的底層原理更甚 所以寫詞文章 讓自己有一個更深的認識

集合是一個龐大的家族 今天先來說說這幾個 ArrayList、LinkedList、Vector 

 

        ArrayList  由於它的底層是陣列 陣列我們都知道它的查詢修改都是效率很高的 ArrayList也是如此 但是為什麼查詢修改效率高 插入和刪除效率低較低呢 這就跟它的資料結構有關係呢 接下啦我們來看看ArrayList資料結構模型

 插入、刪除:如果我們要想集合中插入一個數100 它的操作步驟是 先在集合中把要插入的位置的數32複製一份 然後再把後面的數往後移  我們不僅要複製資料 而且還要將資料往後移 如果這個集合的資料很多的話 那效率就會很低  進行刪除的話後面的資料複製一份 同時資料都要像前面移動 效率也很低

 查詢、修改: 修改如果進行查詢 我們只需要通過陣列下標就可以定位到資料 所以效率高 實際開發中 我們大部分是進行查詢資料 所以ArrayList使用很廣泛

任何事物都有兩面性 不管是生活中 還是工作中 在程式設計也是同樣適用的(因為程式設計也是人發明出來的嘛) 為了解決ArrayList的這一短板 聰明的程式設計師就使用另一個集合

 

 

 ArrayList 增刪改查的原始碼

      從原始碼我們可以看出 不管是插入和刪除元素的時候 ArrayList都會複製陣列操作 這也就導致了它的效率不高

 

 1 //查詢元素
 2  public E get(int index) {
 3 //檢查元素是否越界
 4         rangeCheck(index);
 5 
 6         return elementData(index);
 7     }
 8 
 9 
10 //按順序新增元素
11  public boolean add(E e) {
12        //確認開啟擴容機制 
13         ensureCapacityInternal(size + 1);  // Increments modCount!!
14         elementData[size++] = e;
15         return true;
16     }
17 
18 //在指定位置插入元素
19  public void add(int index, E element) { 
20        //檢查索引是否越界
21         rangeCheckForAdd(index);
22        //確認開啟擴容機制
23          ensureCapacityInternal(size + 1);  // Increments modCount!!
24        //複製陣列
25         System.arraycopy(elementData, index, elementData, index + 1,
26                          size - index);
27       //替換元素
28         elementData[index] = element;
29         size++;
30     }
31 
32 
33 
34 
35 //移除某個元素
36  public E remove(int index) {
37         rangeCheck(index);
38 
39         modCount++;
40         E oldValue = elementData(index);
41 
42         int numMoved = size - index - 1;
43         if (numMoved > 0)
//複製陣列 44 System.arraycopy(elementData, index+1, elementData, index, 45 numMoved); 46 elementData[--size] = null; // clear to let GC do its work 47 48 return oldValue; 49 } 50 51 52

  LinkedList 

       LinkedList它的底層是雙向連結串列實現的非執行緒安全的集合,它是一個連結串列結構,不能像陣列一樣隨機訪問,必須是每個元素依次遍歷直到找到元素為止。其結構的特殊性導致它查詢資料慢。 接下來我們來看看它的結構模型

插入、刪除 :因為是連結串列結構 所以它的插入效率很高 (如果在14 和 18之間插入一個33 的話,連結串列直接會將連線到18的鏈子斷開 然後連線上33所在的前節點  資料18的前節點再連線上33的後節點  如圖2所示) 也就是說 插入一個數字我們只需要將(14 和 18 之間的)連結串列斷開 再將14和33之間的連結串列連上即可  比ArrayList的陣列複製效率高

查詢、修改 :LinkedList 查詢速度慢 因為它要遍歷整個整個集合 直到找到元素為止 如果集合陣列多的話 消耗的資源就多 而ArrayList是通過陣列下標定位速度快 同樣他也是執行緒不安全的

   

linkedList

      在執行查詢時 先判斷元素是靠近頭部還是尾部 如果是頭部 若靠近頭部,則從頭部開始依次查詢判斷 

      執行插入時 判斷是插入到中間還是尾部 如果插入到尾部 直接將尾節點的下一個指標指向新增節點。如果插入到中間 獲取到當前節點的上一個節點(D) 並將D節點的後指標指向新的節點頭指標 然後新增節點的下一個指標指向當前節點。

 1   //查詢元素   
 2   public E get(int index) {
 3       //檢查所引是否越界
 4       checkElementIndex(index);
 5          return node(index).item;
 6      }
 7 
 8 // 返回指定索引處的節點
 9 Node<E> node(int index) {
10   // 指定的索引值與連結串列大小右移一位,及除以 2 進行比較
11    if (index < (size >> 1)) { // 索引小,則從首節點向後掃描,直到索引值處
12    Node<E> x = first;
13     for (int i = 0; i < index; i++)
14      x = x.next;
15     return x;
16     } else { // 索引大,則從尾節點向前掃描,直到索引值處
17     Node<E> x = last;
18      for (int i = size - 1; i > index; i--)
19     x = x.prev;
20     return x;
21   }
22 }
23 
24 
25   //移除指定元素
26    public E remove(int index) {
27          checkElementIndex(index);
28          return unlink(node(index));
29     }
30  
31  //在指定位置新增元素
32    public void add(int index, E element) {
33        //檢查所引是否越界  
34        checkPositionIndex(index);
35        // 在連結串列末尾天新增
36          if (index == size)
37              linkLast(element);
38          else
39              linkBefore(element, node(index));
40     }
41  
42    private static class Node<E> {
43          E item;
44          //頭節點
45          Node<E> next;
46        //尾節點
47          Node<E> prev;
48          Node(Node<E> prev, E element, Node<E> next) {
49             this.item = element;
50              this.next = next;
51              this.prev = prev;
52          }
53      }
54  
55   /**
56       * Links e as last element.
57       */
58      void linkLast(E e) {
59          //用l來臨時儲存未插入前的last節點
60          final Node<E> l = last;
61         //建立一個值為e的新節點 新增第一個元素時 l = null
62          final Node<E> newNode = new Node<>(l, e, null);
63          //將新節點賦值的last
64         last = newNode;
65         if (l == null)
66              first = newNode;
67          else
68             l.next = newNode;
69          size++;
70          modCount++;
71      }

  Vector 

       Vector的資料結構和使用方法 跟ArrayList相同 不同之處在於Vector是執行緒安全的 幾乎所有的對資料操作的方法都被synchronized關鍵字修飾  synchronized是執行緒同步的 當一個執行緒獲得Vector物件鎖的時候 其它的執行緒必須等到它執行完畢之後(鎖被釋放)才能執行  

總結

1.ArrayList 它的底層是一個數組 查詢修改資料快(通過下標定位) 但是插入刪除資料比較慢 (插入資料慢是因為複製陣列耗時)  為了改進這個缺點 於是就有了LinkedList陣列 它是一個連結串列結構 插入和刪除資料很快(只需要修改指標引用) 但是查詢和修改數效率低(他要查詢到整個連結串列從第一個開始尋找 一直找到為止)

2.ArrayList 和LinkedList都是執行緒不安全的

3.Vector是執行緒安全的 但是效率低 當我們執行單個執行緒的時候ArrayList的效率高於Vector 

&n