1. 程式人生 > >List詳細用法與原理解析

List詳細用法與原理解析

List是Collection介面的子介面,他可以定義一個允許重複的線性集合,即允許存放相同的元素,但是存放的位置不同。與collection不同的是,List新增了列表迭代器ListInterator。

與Collection介面相比,List介面新增瞭如下方法:

  • add(int index,E element):在列表指定位置插入指定元素
  • addAll(int index,Collection<? extends E> c):在指定位置新增集合元素
  • get(int index):獲取指定位置的元素
  • indexOf(Object o):獲取元素的第一個位置
  • isEmpty():判斷列表中是否有元素
  • lastIndexOf(Object o):獲取指定元素的最後一個位置
  • listIterator():獲取列表元素的列表迭代器
  • listIterator(int index):從指定位置開始獲取列表迭代器
  • remove(int index):移除指定位置的元素
  • set(int index,E element):用指定元素替換指定位置的元素
  • subList(int fromIdex,int toIndex):獲取fromIndex到toIndex的元素列表

最常使用的List介面的實現類有:ArrayListLinkedList

ArrayList與LinkedList的區別主要有:

1.實現方式不同。ArrayList是基於陣列實現的,而LinkedList是基於連結串列實現的。

2.執行效率。由於實現原理的差異,兩者分別在增、刪、改、查方面各有利弊。ArrayList基於陣列實現,可以根據角標快速的查詢和更新元素,效率高,相反在插入和刪除上效率會慢很多;而LinkedList是基於連結串列實現,可以快速的插入和刪除元素,查詢的效率卻很低。

下面對兩者進行詳細的解析:

ArrayList

使用方法

ArrayList實現了List介面,在List的介面上新增瞭如下方法:

  • colone():返回ArrayList例項的淺表副本
  • ensureCapacity(int minCapacity):增加ArrayList例項的容量。
  • removeRange(int fromIndex,int toIndex):批量移除列表中的元素
  • trimToSize():將列表容量調整至當前大小。
注意:ensureCapacity是不同步的,ArrayList的動態改變列表大小也是基於這個方法實現的。解決不同步的方法是:使用Collections.synchronizedList 方法將該列表“包裝”起來,即List list = Collections.synchronizedList(new ArrayList(...));最好在建立時完成,以防止意外對列表進行不同步的訪問。 儘量不要使用ensureCapacity方法,因為改變list的容量會降低效率,改變list容量的原理是常見一個比之前更大容量的list,將原list中的資料copy到新的list,在刪除原有的list。 實現原理 ArrayList是一個變長的陣列,基本上等同於Vector,但是,ArrayList對writeObjec()t和readObject()方法實現了同步。 1.序列化 ArrayList是用一個Object陣列來儲存資料的。原始碼為:private transient E[] element;。 其中transient 表示element這個屬性不需要自動序列化,因為element儲存的不是真正的元素物件,而是指向物件的地址,序列化地址是沒有意義的,因為序列化的目的是為了反序列化為物件,當對地址進行反序列化後就找不到原來的物件了。所以需要手工的對元素序列化,如下是ArrayList的原始碼實現:
private void writeObject(ObjectOutputStream paramObjectOutputStream)
    throws IOException
  {
    int i = this.modCount;
    paramObjectOutputStream.defaultWriteObject();

    paramObjectOutputStream.writeInt(this.size);

    for (int j = 0; j < this.size; j++) {
      paramObjectOutputStream.writeObject(this.elementData[j]);
    }

    if (this.modCount != i)
      throw new ConcurrentModificationException();
  }

  private void readObject(ObjectInputStream paramObjectInputStream)
    throws IOException, ClassNotFoundException
  {
    this.elementData = EMPTY_ELEMENTDATA;

    paramObjectInputStream.defaultReadObject();

    paramObjectInputStream.readInt();

    if (this.size > 0)
    {
      ensureCapacityInternal(this.size);

      Object[] arrayOfObject = this.elementData;

      for (int i = 0; i < this.size; i++)
        arrayOfObject[i] = paramObjectInputStream.readObject();
    }
  }

2.自動變長機制 當呼叫add方法時,會呼叫ensureCapacityInternal方法進行擴容,每次擴容為原來大小的1.5倍,但是當第一次新增元素或者劉表重元素個數小於10的話,列表容量預設為10. add方法的原始碼如下:
public boolean add(E paramE)
  {
    ensureCapacityInternal(this.size + 1);
    this.elementData[(this.size++)] = paramE;
    return true;
  }

有上述程式碼可以看到,擴容之後將指定元素複製到element陣列中。擴容的原理是:重新建立一個容量是element容量大小1.5倍的陣列,將element中的元素複製到新陣列中,最後將新陣列複製給element,完成擴容。原始碼如下:
private void ensureCapacityInternal(int paramInt)
  {
    if (this.elementData == EMPTY_ELEMENTDATA) {
      paramInt = Math.max(10, paramInt);
    }

    ensureExplicitCapacity(paramInt);
  }

  private void ensureExplicitCapacity(int paramInt) {
    this.modCount += 1;

    if (paramInt - this.elementData.length > 0)
      grow(paramInt);
  }

  private void grow(int paramInt)
  {
    int i = this.elementData.length;
    int j = i + (i >> 1);
    if (j - paramInt < 0)
      j = paramInt;
    if (j - 2147483639 > 0) {
      j = hugeCapacity(paramInt);
    }
    this.elementData = Arrays.copyOf(this.elementData, j);
  }
3.快速失敗迭代器 所謂快速失敗迭代器是指再某個執行緒對列表進行迭代時,不允許其他執行緒對其進行操作,否則會丟擲ConcurrentModificationException異常,只能使用interator的add和remove方法對列表進行結構性的操作。
迭代器iterator()方法的實現是返回一個私有的內部實現類ListItr,這個類繼承了另一個內部類Itr。內部類中提供了add和remove方法。並且呼叫了get(int i ndex)方法來獲取元素。 類Itr的原始碼如下:
private class Itr
    implements Iterator<E>
  {
    int cursor = 0;

    int lastRet = -1;

    int expectedModCount = AbstractList.this.modCount;

    private Itr() {  } 
    public boolean hasNext() { return this.cursor != AbstractList.this.size(); }

    public E next()
    {
      checkForComodification();
      try {
        int i = this.cursor;
        Object localObject = AbstractList.this.get(i);
        this.lastRet = i;
        this.cursor = (i + 1);
        return localObject;
      } catch (IndexOutOfBoundsException localIndexOutOfBoundsException) {
        checkForComodification();
      }throw new NoSuchElementException();
    }

    public void remove()
    {
      if (this.lastRet < 0)
        throw new IllegalStateException();
      checkForComodification();
      try
      {
        AbstractList.this.remove(this.lastRet);
        if (this.lastRet < this.cursor)
          this.cursor -= 1;
        this.lastRet = -1;
        this.expectedModCount = AbstractList.this.modCount;
      } catch (IndexOutOfBoundsException localIndexOutOfBoundsException) {
        throw new ConcurrentModificationException();
      }
    }

    final void checkForComodification() {
      if (AbstractList.this.modCount != this.expectedModCount)
        throw new ConcurrentModificationException(); 
    }
  }

類ListItr原始碼如下:
private class ListItr extends AbstractList<E>.Itr implements ListIterator<E> {
    ListItr(int arg2) { super(null);
      int i;
      this.cursor = i; }

    public boolean hasPrevious()
    {
      return this.cursor != 0;
    }

    public E previous() {
      checkForComodification();
      try {
        int i = this.cursor - 1;
        Object localObject = AbstractList.this.get(i);
        this.lastRet = (this.cursor = i);
        return localObject;
      } catch (IndexOutOfBoundsException localIndexOutOfBoundsException) {
        checkForComodification();
      }throw new NoSuchElementException();
    }

    public int nextIndex()
    {
      return this.cursor;
    }

    public int previousIndex() {
      return this.cursor - 1;
    }

    public void set(E paramE) {
      if (this.lastRet < 0)
        throw new IllegalStateException();
      checkForComodification();
      try
      {
        AbstractList.this.set(this.lastRet, paramE);
        this.expectedModCount = AbstractList.this.modCount;
      } catch (IndexOutOfBoundsException localIndexOutOfBoundsException) {
        throw new ConcurrentModificationException();
      }
    }

    public void add(E paramE) {
      checkForComodification();
      try
      {
        int i = this.cursor;
        AbstractList.this.add(i, paramE);
        this.lastRet = -1;
        this.cursor = (i + 1);
        this.expectedModCount = AbstractList.this.modCount;
      } catch (IndexOutOfBoundsException localIndexOutOfBoundsException) {
        throw new ConcurrentModificationException();
      }
    }
  }

上述原始碼中checkForComodification方法時檢測是否對修改同步,原始碼如下:
final void checkForComodification() {
      if (AbstractList.this.modCount != this.expectedModCount)
        throw new ConcurrentModificationException(); 
    }

其中modCount是記錄列表結構被修改的次數。他的作用是防止列表在迭代期間被惡意修改。 LinkedList 使用方法 LinkedList實現了List介面,在介面的基礎上新增瞭如下方法:
  • addFirst(E e):將指定元素新增到劉表開頭
  • addLast(E e):將指定元素新增到列表末尾
  • descendingIterator():以逆向順序返回列表的迭代器
  • element():獲取但不移除列表的第一個元素
  • getFirst():返回列表的第一個元素
  • getLast():返回列表的最後一個元素
  • offerFirst(E e):在列表開頭插入指定元素
  • offerLast(E e):在列表尾部插入指定元素
  • peekFirst():獲取但不移除列表的第一個元素
  • peekLast():獲取但不移除列表的最後一個元素
  • pollFirst():獲取並移除列表的最後一個元素
  • pollLast():獲取並移除列表的最後一個元素
  • pop():從列表所表示的堆疊彈出一個元素
  • push(E e);將元素推入列表表示的堆疊
  • removeFirst():移除並返回列表的第一個元素
  • removeLast():移除並返回列表的最後一個元素
  • removeFirstOccurrence(E e):從列表中移除第一次出現的指定元素
  • removeLastOccurrence(E e):從列表中移除最後一次出現的指定元素
其中有部分方法實現的功能是一樣的,例如add方法和offer方法實現的功能是一樣的,只是返回的型別不同,add無返回型別,offer返回boolean。remove和poll方法的不同之處是:當列表為空時,poll返回null,而remove會丟擲異常。 實現原理 LinkedList的實現是一個雙向連結串列。在jdk1.6中是一個帶空頭的迴圈雙向連結串列,而在jdk1.7中則變為不帶空頭的雙向連結串列,這從原始碼中可以看出: jdk1.6中定義中屬性為Entry類,如下
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;

jdk1.7中定義屬性為Node類,如下:
transient int size = 0;
  transient Node<E> first;
  transient Node<E> last;
下面以jdk1.7為例。 1.資料結構原理 連結串列中每隔節點包含的內容有:前節點的資訊、本節點資訊、下一個節點的資訊。如下圖所示:
2.私有屬性 正如上述所說,jdk1.7中定義了三個屬性,並且被修飾為transient,分別是
transient int size = 0;
  transient Node<E> first;
  transient Node<E> last;
其中size為連結串列節點數量,first為第一個節點,last為最後一個節點。元素資訊則儲存在Node中,Node的原始碼如下:
private static class Node<E> { E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> paramNode1, E paramE, Node<E> paramNode2) { this.item = paramE;
      this.next = paramNode2;
      this.prev = paramNode1;
    }
  }
3.構造方法 LinkedList定義了兩個構造方法,如下:
  public LinkedList()
  {
  }

  public LinkedList(Collection<? extends E> paramCollection)
  {
    this();
    addAll(paramCollection);
  }

由此可見構造方法呼叫了addAll方法。