接着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();
}
}