Collection读完我们大致上知道这个接口为我们规划处那些接口,下面的几个篇目,就看一看Collection的一些实现,捋一捋大神的代码思路(手动滑稽)。
AbstractCollection是Collection的一个一级实现,具体实现了Collection中的几个通用的方法,之前Collection中说过的方法属性就不再赘述,只看那些新增的东西。
构造器
protected AbstractCollection() {
}
唯一的一个构造器,使用protect修饰,是提供给子类使用。
属性
AbstractCollection中新加了一个属性
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
私有的静态不可变的属性:最大的数组大小。但是这个数字的大小着实让笔者疑惑,因为在下面的代码的阅读过程中我依然能找到,集合返回的最大大小依然有可能是Integer.MAX_VALUE(这个下面会说到),百思不解各种扒之后最终在strackOverflow上找到这样的一些解释:
The shape and structure of an array object, such as an array of int values, is similar to
that of a standard Java object. The primary difference is that the array object has an
additional piece of metadata that denotes the array's size. An array object's metadata,
then, consists of: Class : A pointer to the class information, which describes the object type.
In the case of an array of int fields, this is a pointer to the int[] class.
大概的意思就是说一个数组不仅仅包存数组内部的对象,也用一个元数据保存着数组的其他的信息,
比如数组元素的多少,指向类的信息等等,或者说的专业点叫做头信息Headerwords。
当然也有一定原因是可以在一定程度上避免oom异常
方法
- isEmpty方法
public boolean isEmpty() {
return size() == 0;
}
集合size属性为0是即返回true。
- contains方法
public 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;
}
1、参数允许传入null元素,只有当集合中存在null元素是返回true
2、过程依赖对象的eqauls方法,未重写equals方法拿的比较对象的地址,如果荣写过equals则按照重写的规则进行比较
- toArray方法
public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
toArray的简单版本,通过迭代器的next方法将集合中每一个元素赋值给新的数组,元素比期望少时实际上本质是Arrays.copy方法,元素个数跟期望一样是返回新写的数组,元素个数比期望多时调用本类的 finishToArray方法(这个下面会说到)。
public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
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;
}
toArray的反射版本通过参数的数组类型创建指定类型的数组,处理过程大致一直,不同的一点是多余的位置会用null元素替代,中间的一段代码是为了防止并发的情况出现,一般情况下是不存在这个情况的,初看可能有点看不大懂。
- finishToArray方法
@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;
// overflow-conscious code
if (newCap - MAX_ARRAY_SIZE > 0)
newCap = hugeCapacity(cap + 1);
r = Arrays.copyOf(r, newCap);
}
r[i++] = (T)it.next();
}
// trim if overallocated
return (i == r.length) ? r : Arrays.copyOf(r, i);
}
方法处理的是arrayCopy中新生成的数组,这是的新数组已经装满了元素,但是迭代器中还有多余的元素,这是就需要对这个生成的数组进行扩容,新数组的大小是原数组大小的1.5被再加1,但是当新的数组长度如果大于类中最大的数组大小也就是正整数最大值-8的话,会执行hugeCapacity方法进行数组新长度的设置(下面),之后将结合中的元素迭代完之后便返回新的数组。
- hugeCapacity方法
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;
}
新数组的长度 + 1如果大于正数的最大值,就会变成负数,系统将会提示Required array size too large,接下来新数组的长度 + 1如果比正整数最大值 - 8 大的话,则返回的新数组的长度为正整数的最大值,这个就是我上面产生疑问的原因了。
- add方法
public boolean add(E e) {
throw new UnsupportedOperationException();
}
AbstractCollection类不支持这个方法,交给后面的实现类来实现。
- remove方法
public 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;
}
同contains方法原理大致相同,分null和其他两种区别,同样需要依赖对象的equals方法。
- containsAll方法
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
对参数集合中的每一个元素执行congtains方法,全部都包含才返回true
- addAll方法
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
对参数集合中每一个元素执行add方法,添加成功一个便返回true
- removeAll方法
public boolean removeAll(Collection<?> c) {
//参数的非零校验
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
//参数集合中的元素只要出现在原集合中就把原集合中的该元素删除,成功删除一个将返回的标识位变为true
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
- retainAll方法
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);//非零校验
boolean modified = false;
Iterator<E> it = iterator();
while (it.hasNext()) {
//原集合中元素在参数集合中不存在的话就删除,成功删除一个返回标识位改变为true
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();
}
}
对集合中的每一个元素执行删除方法