JAVA集合原始碼攻堅戰(6)——AbstractList
前言
前面我們已經分析過AbstractCollection和List這兩個介面類了,接下來分析AbstractList。
AbstractList的父母
剛剛靈光一閃,想到一個不太恰當的比喻。比如AbstractList,繼承了AbstractCollection,實現了List介面,那麼AbstractCollection相當於是父親,子承父業,List相當於母親,百善孝為先,應該實現母親的願望(方法)。
子承父業
首先在AbstractCollection中,我們提過,AbstractCollection要求它的實現類一定要Iterator()和size()方法。而在AbstractList中,已經實現了它的Iterator()方法
public Iterator<E> iterator() {
return new Itr(); // Itr類是自己實現的
}
然後AbstractList還自己定義了一個抽象方法
abstract public E get(int index);
從這裡其實就可以看出,get這個方法能根據索引index來返回元素,奠定了List這一系列裡,他的實現類都是有序的,能根據索引來查詢元素。 另外,要實現一個不可修改的列表,程式設計師只需要擴充套件這個類並提供get(int)和size()方法的實現。 要實現可修改的列表,程式設計師必須另外覆蓋set(int, E)方法(否則會丟擲一個UnsupportedOperationException)。 如果列表是可變大小,則程式設計師必須另外覆蓋add(int, E)和remove(int)方法。
實現的方法
1、public int indexOf(Object o) 從頭開始查詢指定元素首次出現的位置
public int indexOf(Object o) { // 獲取一個list迭代器,因為使用預設構造器,當前遊標位置為起始位置0,也即從前往後遍歷 ListIterator<E> it = listIterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return it.previousIndex(); } else { while (it.hasNext()) // 利用equals方法來進行比較 if (o.equals(it.next())) return it.previousIndex(); } return -1; }
注意,這裡的迭代器listIterator是AbstractList自己實現的,我們後面再詳細分析。同時,要注意遊標並不是直接指向元素的,而是位於元素之間,實際當迴圈中的if語句匹配成功時,遊標已經在我們要找的那個元素後面了,所以需要返回的是前一個遊標的位置。 2、public int lastIndexOf(Object o) 從後往前遍歷查詢指定元素首次出現的位置
public int lastIndexOf(Object o) {
// 獲取一個遊標位置從末尾開始的迭代器,使用有引數的構造器
ListIterator<E> it = listIterator(size());
if (o==null) {
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}
3、 public void clear() 清除元素 protected void removeRange(int fromIndex, int toIndex) 範圍刪除元素
public void clear() {
removeRange(0, size());
}
protected void removeRange(int fromIndex, int toIndex) { // 刪除包含fromIndex,不包含toIndex的範圍內的元素
// 獲取一個遊標位於fromIndex元素之前的迭代器
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) { // 通過i<n判斷,不會刪除toIndex該位置的元素
it.next();
it.remove();
}
}
4、public boolean addAll(int index, Collection<? extends E> c) 在指定的索引位置新增元素,新增的元素順序按照給定的集合的迭代器返回元素順序
public boolean addAll(int index, Collection<? extends E> c) {
// 索引越界檢查
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
// 需要重寫add(E)和add(index,E)方法,否則丟擲UnsupportedOperationException異常
add(index++, e);
modified = true;
}
return modified;
}
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size())
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
迭代器
AbstractList自己內部實現了兩個迭代器:Itr和ListItr。 ListItr本身也是繼承了Itr的。我們先來看Itr
Itr迭代器
先來看完整的實現程式碼
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
* 指向後續呼叫next()方法返回的那個元素的索引
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
* 指向最近一次呼叫next或previous方法返回的元素的索引。如果該元素被刪除了,則重置為-1。
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
* modCount這個變數是後續的list實現類應該有的。如果違反了這一點,迭代器會出現併發的修改操作。
*/
int expectedModCount = modCount;
// 實現了Iterator介面的hasNext方法
public boolean hasNext() {
return cursor != size();
}
// 實現了Iterator介面的next方法
public E next() {
// 併發操作檢查,如果在獲取元素的時候,對元素進行修改,則丟擲異常
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
// 實現了Iterator介面的remove方法
// 根據Iterator對remove的描述,該方法應該只有在呼叫過一次next方法後,才能呼叫該方法。
public void remove() {
// 如果lastRet<0,說明之前並沒有呼叫過next,或者已經被移除,直接丟擲異常。
if (lastRet < 0)
throw new IllegalStateException();
// 檢查是否有併發操作
checkForComodification();
try {
// 呼叫AbstractList的子類實現的remove(index)方法
AbstractList.this.remove(lastRet);
if (lastRet < cursor)// 如果刪除的元素的索引在當前遊標前面,那麼刪除元素後,遊標要向前移動一個單位
cursor--;
lastRet = -1;// 重置為-1
expectedModCount = modCount; // 一個修改操作結束後,重新設定檢查併發的引數的值。
//其實這是避免子類實現了其他方法,會修改modCount值,
//造成expectedModCount與modCount不一致
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
// 檢查併發修改操作
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ListItr迭代器
// 繼承Itr,實現了ListIterator,是對Itr的一個增強版
// ListItr 實現了ListIterator,支援雙向遍歷
private class ListItr extends Itr implements ListIterator<E> {
// 構造器,直接設定遊標指向的位置
ListItr(int index) {
cursor = index;
}
// 判斷前面是否還有元素
public boolean hasPrevious() {
return cursor != 0;
}
// 獲取前一個元素
public E previous() {
// 檢查併發操作
checkForComodification();
try {
int i = cursor - 1; // 遊標向前移動一位
E previous = get(i); // 呼叫子類的get(index)方法
lastRet = cursor = i; // 返回剛剛查詢的那個元素的索引
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
// 和remove類似,
// 根據ListIterator對set的描述,該方法應該只有在呼叫過一次next或者previous後,才能呼叫該方法。
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); // 檢查併發操作
try {
// 呼叫實現類的set(index,E)方法
AbstractList.this.set(lastRet, e);
expectedModCount = modCount; // 操作記錄數設定
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
// 在當前遊標位置,呼叫子類的add方法插入元素
AbstractList.this.add(i, e);
lastRet = -1; // 重置lastRet
cursor = i + 1; // 插入元素後,遊標位置向後移動一位
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}