接著Java集合框架學習---深入探究ArrayList源碼(二)繼續學習ArrayList源碼。
- removeAll函數
public boolean removeAll(Collection<?> c):
保留差集。
拓展(什么叫集合的差集?):一般地,記A,B是兩個集合,則所有屬于A且不屬于B的元素構成的集合,叫做集合A減集合B(或集合A與集合B之差),類似地,對于集合A、B,我們把集合{x∣x∈A,且x?B}叫做A與B的差集。
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
removeAll方法里面還有兩個方法:
1.Objects.requireNonNull函數:
用來判斷傳入的集合是否為空,若為空則拋出一個空指針異常
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
2.batchRemove函數:
//批量刪除,complement為false時刪除數組緩沖區中集合c包含的元素;
//complement為true時,刪除集合c中不包含的元素;
//可以用實現交,差等集合運算
//使用復制的方法,而不是調用remove()的方式,減少了元素的重復復制
//同樣要將newSize之外的元素引用置空,防止內存泄漏
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
//不變式,w <= r
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
//函數對集合中的元素進行遍歷,首先復制集合中的元素,然后檢查是否符合complement的要求進行保留。
//在finally中,復制元素到集合中。并修改相應的size。
//如果有異常拋出,保存好還未處理的數組元素。
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
- retainAll函數
public boolean retainAll(Collection<?> c) :
保留交集
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
- subList函數
public List<E> subList(int fromIndex, int toIndex):
返回列表的一部分--從fromIndex到toIndex之間的部分(包含formIndex對應的元素但不包含toIndex對應的元素)。需要注意的是,由于并沒有像subString一樣生成一個新的對象,subList函數返回的是原列表的一個視圖,因此它所有的操作最終都會作用在原列表上。
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
subList函數先進行越界判斷,如果下標合法則返回一個SubList對象。this代表的是原始List,即調用subList函數的List。
subListRangeCheck函數是用來進行越界判斷的:
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
SubListt類是ArrayList的內部類。它和ArrayList一樣,都繼承了AbstractList類以及實現了RandomAccess接口,同時也提供了get、add、set、remove等方法。
下面我們來看看subList類的源碼:
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
//看到這里就能理解為什么對SubList實例的操作會影響到父列表:
//因為子列表的處理僅僅是給出了一個映射到父列表相應區間的引用
//再加上final的修飾,就能明白為什么進行了截取子列表操作之后
//父列表不能刪除SubList中的首個元素了--因為offset不能更改
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
//在某個位置設置新的值并返回原來元素的值
public E set(int index, E e) {
rangeCheck(index);
//越界檢查
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
//獲得某個位置的元素的值
public E get(int index) {
rangeCheck(index);
//越界檢查(ArrayList只要是涉及到訪問index的操作就會有類似的越界檢查)
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
public int size() {
checkForComodification();
return this.size;
}
//添加元素
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
//對子列表的添加元素的操作實際上是對父列表進行添加元素的操作
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
//刪除某個位置的元素
public E remove(int index) {
rangeCheck(index);
checkForComodification();
//對子列表中的元素進行刪除實際上是對父列表中的元素進行刪除操作
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
//刪除某一部分的元素(從fromIndex到toIndex)
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
public boolean addAll(Collection<? extends E> c) {
return addAll(this.size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
parent.addAll(parentOffset + index, c);
this.modCount = parent.modCount;
this.size += cSize;
return true;
}
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>() {
int cursor = index;
int lastRet = -1;
int expectedModCount = ArrayList.this.modCount;
public boolean hasNext() {
return cursor != SubList.this.size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= SubList.this.size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[offset + (lastRet = i)];
}
public boolean hasPrevious() {
return cursor != 0;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[offset + (lastRet = i)];
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = SubList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[offset + (i++)]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
SubList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(offset + lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
SubList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}
};
}
可以看出,SubList類中的所有操作列表的函數,都會對原列表結構進行操作。但是如果我們對原列表進行操作,卻不會影響到調用subList函數之后返回的子列表。所以這樣一來就容易發生不確定性錯誤。為了避免一些未知的錯誤,這里也引入了fail-fast機制(快速失敗機制)。
關于fail-fast機制我以后再開博客介紹,這里就不花篇幅來講了
- forEach函數
public void forEach(Consumer<? super E> action):
用來遍歷ArrayList的函數(還沒搞懂里面具體的實現,先放這里,以后再埋坑)
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}