概述首先AbstractCollection是java自己提供的一个最基本的Collection的实现。当然它依然是一个抽象类。
对于一个不可更改的集合,只要继承这个类并且实现迭代器和size()方法就行。
对于一个可更改的集合,需要实现add和返回Iterator的方法,当然可选的实现remove方法
通常应该提供两个构造器,一个无参的,一个是包含集合元素的
public Object[] toArray()
这个方法返回一个包含集合内所有元素的数组。这个数组的顺序必须是与根据迭代器产生的是一致的。这样就可以根据迭代器的实现来确定顺序。
这个方法是安全的,也就是说这个方法会自己产生一个新的数组,并且返回的这个数组必须是可以被调用者修改
这个方法是基于数组和基于集合的API之间的桥梁
这个方法的实现还确保了即使在迭代器期间修改了集合。比如说size的返回的大小不对还是会根据迭代器迭代到的元素的数量得到正确的数组。
总的来说实现算是比较简单的。关键在于如何实现size返回不正确的时候的数组生成。这里使用了两个内部的静态函数
private static <T> T[] finishToArray(T[] r, Iterator<?> it)
private static int hugeCapacity(int minCapacity)
首先对于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;
}
思路上说:
这里有几个有意思的地方
- 首先一个是使用三目运算来调整返回值,比使用if方法显得简洁的多。我之前很少用这样的方法来实现返回值。需要说明的是if和三目运算的一个重大的区别是三目运算是必须要返回值的。
- 二个有意思的地方,迭代顺序是根据具体的迭代器的实现来确定的,这就给了编程人员比较大的自由,比如说可以在这个基础上在写一个由迭代器参数的toArray方法。这样就可以从集合中间写迭代器
- 第三个有意思的地方是这个返回的数组是Object类数组。也就说在使用的时候可能需要强制类型转换。
- 第四个有意思的地方使我很好奇,这个方法应该是没有办法保证集合中间不产生的空隙的,比如说集合中的元素是这样的[1,null,2]这样的话,迭代器是否可以正确的实现呢?是添加一个null到数组还是只填入1而忽略后面的。不过这个类是个抽象类,没办法直接实例化,具体还是等看到后面的可实例化的类在说吧。
然后是第一个静态方法:
@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);
}
从思路上说
看几个有意思的地方:
- 首先有意思的是这里返回的是T类型的数组,而toArray实现使用的是Object数组。不知道是不是历史原因或者有其他特殊的考量?
- 其次为什么是cap=cap+cap>>1+1。加一当然好解释,防止cap等于2,但是为何不是使用ca<<1+1或者类似这种,这种似乎是更常用的扩展数组的方式,是否是因为通常size不会比迭代器迭代的元素小太多。
- 数组的大小的极限也是个有意思的地方。
最后一个是用于确定确定数组极限的值:
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;
}
思路:
看几个有意思的地方:
- 这里解释了为了输入参数为cap+1,因为需要考虑cap是否可以在分配,
- 使用个MAX_ARRAY_SIZE和MAX_VALUE 中的满足条件的值,应该是为了适应不同虚拟机的条件,当然也可能有其他理由,但是目前我还想不到其他理由这样做。
public <T> T[] toArray(T[] a)
这个方法将对象先试图填充到给定的数组a中,如果a的数组不够用了,则新建一个新数组来保存集合中的元素
@SuppressWarnings("unchecked")
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;
}
思路:
一个有意思的地方使这里的其实需要比较3个量,实际的迭代器迭代数量,传入的参数a的大小,以及size返回的大小。当然根据这里具体的实现来说,作者认为应该先判断size和传入参数的大小的哪个大,选用大的数组,当然这样的方式如果当实际迭代器迭代<传入参数a的长度< size的时候这样是多做了一个数组,但是综合考虑可能是一种较优的方案。
这个就比较简单了:
需要注意的一点是这个类需要判断参数是否是null。并分情况讨论。
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;
}
其实这种策略只是一个简单的代表
不仅仅是contains对于
remove也需要一样的策略,需要确定是不是null。
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;
}
剩下的方法没有需要特别注意的地方。就省略了。
原文:http://blog.csdn.net/u011518120/article/details/51924587