AbstractColletion源码分析
简介
AbstractCollection直接实现了Collection接口,我们来了解一下。
API
如图可知除了finishToArray(T[],Iterator<?>),hugeCapacity以及受保护的构造方法,其他都是继承自Collection以及Object,并给与实现,如果不清楚Collection接口的小伙伴,可以先去阅读上篇Collection接口再来继续下面的内容。
源码分析
- 返回迭代器,交于子类去实现
public abstract Iterator<E> iterator();
- 返回长度,交于子类去实现
public abstract int size();
- 判断是否为空
public boolean isEmpty() {
return size() == 0;
}
-
判断是否包含某个元素
1:如果传进来的对象是null,则判断集合中是否包含null
2:如果传进来的对象不是null,则判断集合中是否有相同的元素
public boolean contains(Object o) {
// 获取迭代器
Iterator<E> it = iterator();
// 对传进来的对象的非空判断
if (o==null) {
// 表示集合中存在null值
while (it.hasNext())
// 如果下一个元素是null,则返回true
if (it.next()==null)
return true;
} else {
// 表示O不为空
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
-
将集合转成Object[]
1:返回一个数组,跟集合元素的顺序一样。
2:如果迭代器返回的元素数量大于指定的数组,则分配新的数组(可能存在并发操作,导致集合元素元素变化)
3:在这里会分两种情况:如果集合元素的长度最终小于等于分配的数组r的长度,那么调用Arrays.copyOf方法; 如果集合元素的长度大于分配的数组r的长度,也就是it.hasNext() ==true ,因为预期情况 当for循环完了之后,it.hasNext ==false, 当为true的时候,直接调用finshToArray方法重新分配r数组的容量,去容纳多余的集合元素。
public Object[] toArray() {
// 新建一个Object[]数组 ,长度为集合长度
Object[] r = new Object[size()];
// 获取迭代器
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext())
// 如果要复制的元素小于等于r数组的长度,调用Ayyarys.copyOf方法
return Arrays.copyOf(r, i);
r[i] = it.next(); // 指针后移
}
// 如果集合元素比预期的多,那么调用finshToArray,否则返回数组r
return it.hasNext() ? finishToArray(r, it) : r;
}
- 集合转Object[]
@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()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} 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();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
-
finishToArray (完成toArray的后续方法)
1:当迭代器返回的元素多于预期时,重新分配在toArray中使用的数组容量,即创建一个新的数组,将原来数组的内容复制到新数组中,并追加集合新增的元素。
2: 扩容的具体内容: hugeCapacity扩大数组最大容量。 之后再进行复制collection元素到数组里,此时的r就拥有了和newcap一样的容量, 然后r开始从i++(r之前的length往后开始添加新的元素) 但是估计一般不需要再次扩大数组容量, 当然这样设计也体现了设计的严密性。 最后看(i是最终元素量)有没有达到 扩展之后的长度, 假如正好,直接返回r数组,没有根据i的最终元素量截断。
@SuppressWarnings("unchecked")
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
// 获取传进来的数组大小
int i = r.length;
// 判断是否还有元素(这里一个关键点,传进来的迭代器,指在哪里就是哪里的位置)
// 所以it.hasNext()并不是第一个位置,而是传过来的下一个位置
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);
}
-
hugeCapacity
判断是否溢出,溢出则抛出 OutOfMemoryError错误
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError
("Required array size too large");
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
- add - 添加方法(可选)
// 一个典型的可选方法,如果子类不实现,那么直接抛出异常UnsupportedOperationException
public boolean add(E e) {
throw new UnsupportedOperationException();
}
- remove - 移除某个元素
// 删除某个元素
public boolean remove(Object o) {
Iterator<E> it = iterator();
// 如果为null,循环判断并删除
if (o==null) {
while (it.hasNext()) {
if (it.next()==null) {
it.remove();
return true;
}
}
} else {
// 不为null,循环判断是否等同元素
while (it.hasNext()) {
if (o.equals(it.next())) {
it.remove();
return true;
}
}
}
return false;
}
- containsAll - 判断列表是否包含集合c的所有元素
// 判断列表是否包含集合中的所有元素
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
// 只要有一个不存在于列表就返回false
return false;
return true;
}
- addAll - 将集合所有元素添加到列表中
// 将集合中的所有元素添加到列表中
public boolean addAll(Collection<? extends E> c) {
// 标记是否修改
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
- removeAll - 移除集合中存在于列表的所有元素
public 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;
}
-
retainAll - 从列表a中移除未包含在指定c集合中的所有元素
这部分内容在此文章中有详细讲到:https://blog.csdn.net/pulong0748/article/details/79199080
public boolean retainAll(Collection<?> c) {
// 判空,空则抛出空指针异常
Objects.requireNonNull(c);
boolean modified = false;
Iterator<E> it = iterator();
while (it.hasNext()) {
// 具体实现看子类,只是调用了Collection接口的方法
// 如果包含c集合中的元素,则从列表中删除
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
- clear 清空所有元素
// 删除所有元素
public void clear() {
Iterator<E> it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}
- toString 将列表转成字符串
// 把列表转成String
public String toString() {
Iterator<E> it = iterator();
// 如果空集合则返回"[]"
if (! it.hasNext())
return "[]";
// Stringbuilder连接字符串
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(' ');
}
}
Arrays.copyOf和System.arraycopy
1: 判断类型是否相匹配,如果匹配则直接新建数组,如果不匹配通过反射新建数组
2:Arrays.copyof底层调用的是System.arraycopy()方法3:native指的是本地方法,在这里的arraycopy是由C++实现的
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
@param <U> 原始数组中对象的类
@param <T> 返回数组中对象的类
@param original 要被复制的原始数组
@param newLength 要返回的数组的长度
@param newType 要返回的对象数组的类型
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
// getComponentType 用来获取对象数组的class对象,非数组类型不可通过getComponentType来获取class对象。
// 根据原始数组传过来的类型进行判断,返回类型相同的对象数组
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
// 调用System.arraycopy来完成数组拷贝功能
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
// System.arraycopy方法
@Param src - 源数组。
@Param srcPos - 源数组中的起始位置。
@Param dest - 目标数组。
@Param destPos - 目标数据中的起始位置。
@Param length - 要复制的数组元素的数量。
// 将指定源数组中的数组从指定位置开始复制到目标数组的指定位置
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);