前言
(PS:上周由于准备自考,更新延误了一些,见谅!)
AbstractCollection类是Collection接口的一个实现类,它只是为了给我们提供了一些最基础的接口的实现,在实际的应用中,我们并不会去使用这个类来封装一些信息,接下来我们首先去看一下这个类的继承关系图。
结构图

由此可以看出AbStractCollection只是实现了Collection接口,并没有实现或者继承其他的类或者接口。接下来,我们来看一下这个类的源码,看看会有什么收获。
源码
package java.util;//TAG 1public abstract class AbstractCollection<E> implements Collection<E> {protected AbstractCollection() {}public abstract Iterator<E> iterator();public abstract int size();//TAG 2public boolean isEmpty() {return size() == 0;}//TAG 3public boolean contains(Object o) {Iterator<E> it = iterator();if (o==null) {while (it.hasNext())if (it.next()==null)return true;} else {while (it.hasNext())if (o.equals(it.next()))return true;}return false;}//TAG 3public Object[] toArray() {Object[] r = new Object[size()];Iterator<E> it = iterator();for (int i = 0; i < r.length; i++) {if (! it.hasNext())return Arrays.copyOf(r, i);r[i] = it.next();}return it.hasNext() ? finishToArray(r, it) : r;}//TAG 3@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {int size = size();T[] r = a.length >= size ? a :(T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);Iterator<E> it = iterator();for (int i = 0; i < r.length; i++) {if (! it.hasNext()) {if (a == r) {r[i] = null;} else if (a.length < i) {return Arrays.copyOf(r, i);} else {System.arraycopy(r, 0, a, 0, i);if (a.length > i) {a[i] = null;}}return a;}r[i] = (T)it.next();}return it.hasNext() ? finishToArray(r, it) : r;}//TAG 4private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//TAG 5@SuppressWarnings("unchecked")private static <T> T[] finishToArray(T[] r, Iterator<?> it) {int i = r.length;while (it.hasNext()) {int cap = r.length;if (i == cap) {int newCap = cap + (cap >> 1) + 1;if (newCap - MAX_ARRAY_SIZE > 0)newCap = hugeCapacity(cap + 1);r = Arrays.copyOf(r, newCap);}r[i++] = (T)it.next();}return (i == r.length) ? r : Arrays.copyOf(r, i);}//TAG 6private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError("Required array size too large");return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}//TAG 7public boolean add(E e) {throw new UnsupportedOperationException();}//TAG 3public boolean remove(Object o) {Iterator<E> it = iterator();if (o==null) {while (it.hasNext()) {if (it.next()==null) {it.remove();return true;}}} else {while (it.hasNext()) {if (o.equals(it.next())) {it.remove();return true;}}}return false;}//TAG 3public boolean containsAll(Collection<?> c) {for (Object e : c)if (!contains(e))return false;return true;}//TAG 3public boolean addAll(Collection<? extends E> c) {boolean modified = false;for (E e : c)if (add(e))modified = true;return modified;}//TAG 3public boolean removeAll(Collection<?> c) {Objects.requireNonNull(c);boolean modified = false;Iterator<?> it = iterator();while (it.hasNext()) {if (c.contains(it.next())) {it.remove();modified = true;}}return modified;}//TAG 3public boolean retainAll(Collection<?> c) {Objects.requireNonNull(c);boolean modified = false;Iterator<E> it = iterator();while (it.hasNext()) {if (!c.contains(it.next())) {it.remove();modified = true;}}return modified;}//TAG 3public void clear() {Iterator<E> it = iterator();while (it.hasNext()) {it.next();it.remove();}}//TAG 3public String toString() {Iterator<E> it = iterator();if (! it.hasNext())return "[]";StringBuilder sb = new StringBuilder();sb.append('[');for (;;) {E e = it.next();sb.append(e == this ? "(this Collection)" : e);if (! it.hasNext())return sb.append(']').toString();sb.append(',').append(' ');}}}
TAG 1 :
从关键字abstract可以看出这个类是一个抽象类,这个类只是实现了一部分的接口,还遗留了一部分接口需要我们在子类中去实现。
TAG 2 :
isEmpty()方法通过size()接口是否等于0来判断,而size()接口的实现交给了更为具体的集合类去实现。
TAG 3 :
contains(),remove(),containsAll(),addAll(),removeAll(),retainAll(),clear(),toString()这些方法我们通过阅读源码可以知道,都是通过迭代器来实现的,其中有一些方法使用了增强for循环,但其实编译器会将增强for循环编译为使用迭代器的遍历操作。
TAG 4 :
数组作为一个对象,需要一定的内存存储对象头信息,对象头信息最大占用内存不可超过8 byte。
TAG 5 :
finishToArray(T[] r, Iterator<?> it)方法用于数组扩容,当数组索引指向最后一个元素+1时,对数组进行扩容:即创建一个大小为(cap + cap/2 +1)的数组,然后将原数组的内容复制到新数组中。扩容前需要先判断是否数组长度是否溢出。这里的迭代器是从上层的方法(toArray(T[] t))传过来的,并且这个迭代器已执行了一部分,而不是从头开始迭代的
TAG 6 :
hugeCapacity(int minCapacity)方法用来判断该容器是否已经超过了该集合类默认的最大值即(Integer.MAX_VALUE -8),一般我们用到这个方法的时候比较少,后面我们会在ArrayList类的学习中,看到ArrayList动态扩容用到了这个方法。
TAG 7 :
这里的add(E)方法默认抛出了一个异常,为什么这样去定义呢?而不是直接定义为抽象方法?
如果我们想修改一个不可变的集合时,抛出 UnsupportedOperationException 是正常的行为,比如当你用 Collections.unmodifiableXXX() 方法对某个集合进行处理后,再调用这个集合的修改方法(add,remove,set…),都会报这个错。因此 AbstractCollection.add(E) 抛出这个错误是准从标准。
而之所以没有定义为抽象方法,是因为可能有很多地方用不到这个方法,用不到还必须实现,这岂不是让人很困惑么。(PS:参考自拭心)
toArray详解
在这个类中,我们需要详细了解的方法是toArray,正如其名,它可以把一个集合转换为数组,可以看到toArray方法分为了两种参数的方法:
/*** 分配了一个等大空间的数组,然后依次对数组元素进行赋值*/public Object[] toArray() {//新建等大的数组Object[] r = new Object[size()];Iterator<E> it = iterator();for (int i = 0; i < r.length; i++) {//判断是否遍历结束,以防多线程操作的时候集合变得更小if (! it.hasNext())return Arrays.copyOf(r, i);r[i] = it.next();}//判断是否遍历未结束,以防多线程操作的时候集合变得更大,进行扩容return it.hasNext() ? finishToArray(r, it) : r;}/*** 泛型方法的`toArray(T[] a)`方法在处理里,会先判断参数数组的大小,* 如果空间足够就使用参数作为元素存储,如果不够则新分配一个。* 在循环中的判断也是一样,如果参数a能够存储则返回a,如果不能再新分配。*/@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {int size = size();//当数组a的长度大于等于a,直接将a赋予给r,否则使用反射API获取一个长度为size的数组T[] r = a.length >= size ? a :(T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);Iterator<E> it = iterator();for (int i = 0; i < r.length; i++) {//判断是否遍历结束if (! it.hasNext()) {//如果 a == r,将r的每项值赋空,并将a返回if (a == r) {r[i] = null;} else if (a.length < i) {//如果a的长度小于r,直接调用Arrays.copyOf进行复制获取一个新的数组return Arrays.copyOf(r, i);} else {System.arraycopy(r, 0, a, 0, i);if (a.length > i) {a[i] = null;}}return a;}//如果遍历结束,将迭代器获取的值赋给rr[i] = (T)it.next();}//判断是否遍历未结束,以防多线程操作的时候集合变得更大,进行扩容return it.hasNext() ? finishToArray(r, it) : r;}/*** 设定该容器的最大值*/private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/*** 用于动态扩容*/@SuppressWarnings("unchecked")private static <T> T[] finishToArray(T[] r, Iterator<?> it) {int i = r.length;while (it.hasNext()) {int cap = r.length;if (i == cap) {int newCap = cap + (cap >> 1) + 1;if (newCap - MAX_ARRAY_SIZE > 0)newCap = hugeCapacity(cap + 1);r = Arrays.copyOf(r, newCap);}r[i++] = (T)it.next();}return (i == r.length) ? r : Arrays.copyOf(r, i);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError("Required array size too large");return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}
为了帮助了解,我把Arrays.copyOf(r.i)的源码也贴出来:
//参数original代表你传入的需要复制的泛型数组,newLength复制得到数组的大小
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
我们可以观察到其中调用了System.arraycopy方法,为了保持刨根问底的态度,我们又去翻看了这个方法的源码:
//src数组里从索引为srcPos的元素开始, 复制到数组dest里的索引为destPos的位置, 复制的元素个数为length个.
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos,int length);
可以看到这个方式是由关键字native修饰的方法,那么native修饰的方法有什么含义呢?
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。
然后我们来分析toArray()中需要注意的点,通过原源码中的英文注解,toArray得到的数组跟原collection没有任何关系,我们可以对数组的每个引用值做修改,而不会影响到原collection.这个看起来好像是多余说明的,但是考虑到ArrayList其实就是基于数组实现的,那这个限制保证了即使是将ArrayList转化为数组,那也应该是分配一个新数组,而不是返回原来的数组。
如果我们在单线程操作的情况下,collection集合大小不变,正常应该是执行到 return it.hasNext() ? finishToArray(r, it) : r;这条语句结束,但考虑到在复制的过程中,collection的集合可能会有变化,可能是变大也可能是变小,所以方法增加了对这种情况的处理,这就是为什么每次循环都要判断是collection是否遍历完,以及最后再判断collection是否变得更长,如果是的话,还需要重新再为array分配空间。
通常情况下,我们不会执行到hugeCapacity,但作为一个框架来说,这体现了设计时的严谨。
下节预告
下节课,我们会刨根问底的去了解List的使用。
公众号
扫码或微信搜索 Vi的技术博客,关注公众号,不定期送书活动各种福利~

