List詳細用法與原理解析
阿新 • • 發佈:2019-01-07
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介面的實現類有:ArrayList和LinkedList。
ArrayList與LinkedList的區別主要有:
1.實現方式不同。ArrayList是基於陣列實現的,而LinkedList是基於連結串列實現的。
2.執行效率。由於實現原理的差異,兩者分別在增、刪、改、查方面各有利弊。ArrayList基於陣列實現,可以根據角標快速的查詢和更新元素,效率高,相反在插入和刪除上效率會慢很多;而LinkedList是基於連結串列實現,可以快速的插入和刪除元素,查詢的效率卻很低。
下面對兩者進行詳細的解析:
ArrayList
使用方法
ArrayList實現了List介面,在List的介面上新增瞭如下方法:
- colone():返回ArrayList例項的淺表副本
- ensureCapacity(int minCapacity):增加ArrayList例項的容量。
- removeRange(int fromIndex,int toIndex):批量移除列表中的元素
- trimToSize():將列表容量調整至當前大小。
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):從列表中移除最後一次出現的指定元素
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方法。