Java 集合类 源码分析学习
—-(2)集合基本接口的实现类(AbstractCollection、AbstractList、AbstractDeque、AbstractMap、AbstractMap)
写在最全面:
直接上图:AbstractCollection类是唯一一个直接实现Collection接口的子类,是几乎所有java数据结构类的父类,提供基本的集合操作,以供子类复用。AbstractList、AbstractDeque、AbstractMap、AbstractMap同理,提供对应数据容器的基本操作,以供子类调用。
1. AbstractCollection
AbstractCollection类实现了contains、toArray、remove()等方法;
未实现方法( 2个抽象方法 和 1个add()个方法直接抛出异常需要重写 ):
public abstract Iterator<E> iterator();
public abstract int size();
//直接调用将抛出 ---- 不支持操作异常
public boolean add(E e) {
throw new UnsupportedOperationException();
}
已实现的方法包括以下几个大类:
(1)增加(add、addAll)
add(E e)方法:需要子类重写,直接调用将会抛出异常。
public boolean add(E e) {
throw new UnsupportedOperationException();
}
addAll(Collection<? extends E> c) 方法:将添加外部集合c中的所有元素到此集合中,基本思想就是:遍历外部集合 c 的所有元素,逐个调用add(E e)添加到本集合中。显然,子类没有实现 add(E e) 方法,调用此方法也会抛出异常UnsupportedOperationException。
public boolean addAll(Collection<? extends E> c) {
// 是否添加到本集合标志位,只要有一个元素添加成功,modified = true
boolean modified = false;
// 遍历集合 调用add方法
for (E e : c) {
if (add(e)) {
modified = true;
}
}
return modified;
}
(2)删除(remove、removeAll、clear、retainAll)
remove(Object o)方法:基本思想就是,获取本集合的迭代器 –> 遍历迭代器 –> 找到与对象 o 相等的元素 –> 调用迭代器的remove() 方法移除此元素。
在判断元素相等时,分为null和非null两种情况:为null时,直接 = = 判断;非null时,调用equals方法。
源码中可以看出,remove(Object o) 方法对于集合中存在的多个与对象 o 相等的元素,只移除其中中第一个。
PS:迭代器的遍历之前已经提到过,具体使用见 Java 集合类 源码分析学习 —-(1)总体认识 中的2.1章节。
public boolean remove(Object o) {
Iterator<E> it = iterator();
// 对象o为null的情况
if (o==null) {
// 遍历迭代器
while (it.hasNext()) {
if (it.next()==null) {
// 调用迭代器的remove() 方法
it.remove();
// 只删除完第一个,立刻返回
return true;
}
}
} else {
while (it.hasNext()) {
if (o.equals(it.next())) {
it.remove();
return true;
}
}
}
return false;
}
removeAll(Collection<?> c) 方法,移除与外部集合 c 中相等的元素:与add和addAll 方法相类似,获取迭代器,遍历,组个调用remove方法,不多赘述,直接看代码:
public boolean removeAll(Collection<?> c) {
// 外部集合 c 判空
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
// 遍历本集合
while (it.hasNext()) {
// 元素存在于外部集合 c 中,直接从迭代器中移除
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
retainAll(Collection<?> c) 方法,与removeAll 相反,保留与外部集合 c 中相等的元素,移除其他所有元素:遍历时,多了个 !操作。
public 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;
}
clear() 方法,删除集合中所有元素:获取迭代器 –> 遍历 –> 逐个删除。
public void clear() {
Iterator<E> it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}
(3)查询(contains、containsAll、iterator、size、isEmpty)
contains(Object o)、containsAll(Collection<?> c) : == 对比 remove(Object o)、removeAll(Collection<?> c) 方法 == ,直接上代码 + 注释:
public boolean contains(Object o) {
/** 获取迭代器 */
Iterator<E> it = iterator();
/** o 为 null 值时,判断 o == null
* o 不为 null 值时,判断 o.equals(it.next())
* 利用迭代器的 hasNext + while 遍历
* */
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;
}
public boolean containsAll(Collection<?> c) {
for (Object e : c) {
// 只要有一个 外部集合的元素 不包含在 此集合中,就 return false
if (!contains(e)) {
return false;
}
}
return true;
}
iterator() 和 size() 方法,为迭代器和集合大小方法,最开始已经提到为抽象方法 :
public abstract Iterator<E> iterator();
public abstract int size();
isEmpty() 方法,判断集合是否为空:直接判断 size() 是否为 0即可
public boolean isEmpty() {
return size() == 0;
}
(4)转数组(toArray()、toArray(T[] a))
toArray() 方法,按集合中的顺序返回一个包含集合中所有元素的数组:
源码中,调用了 Arrays.copyOf 方法,复制集合中内容,所以修改数组中的内容,不会改变集合中的元素。
具体过程见 代码 + 注释:
/ **
* 此处明明初始化了一个与集合的大小相等的数组,却在之后还要判断迭代器的内容 ?
* 考虑到多线程情况下,迭代器内容发生改变
* /
public Object[] toArray() {
/** 初始化一个数组,大小 = 集合的大小 */
Object[] r = new Object[size()];
/** 获取集合的迭代器 */
Iterator<E> it = iterator();
/** 遍历迭代器 */
for (int i = 0; i < r.length; i++) {
/**当 迭代器内容 < 初始化数组大小 时,
* 调用Arrays.copyOf(),
* 复制到新数组中,返回 */
if (! it.hasNext()) {
return Arrays.copyOf(r, i);
}
r[i] = it.next();
}
/**当 迭代器内容 >初始化数组大小 时,
* 调用 finishToArray(r, it)扩容数组并复制,
* 复制到新数组中,返回 */
return it.hasNext() ? finishToArray(r, it) : r;
}
其中用到的 finishToArray 方法:
每次到达数组最大容量是进行扩容,扩容大小为:cap + cap * 2 + 1
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);
}
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;
}
toArray(T[] a) 方法, 同样是返回一个包含集合中所有元素的数组:如果数组 a 大小大于集合大小,则返回其中;否则重新分配内存空间,将新内存的引用交给 a 返回。
public <T> T[] toArray(T[] a) {
/** 集合大小 */
int size = size();
/** 数组 a 的长度 小于 集合的大小 : 利用反射 new 一个大小为 size 为数组,引用赋给 r
数组 a 的长度 大于等于 集合的大小 : a 的引用 赋给 r
*/
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 也就是,a.length >= size() ,数组大小 大于 集合大小 */
if (a == r) {
/** 将剩下的数组中 size - (a.length - 1) 的元素赋值 null */
r[i] = null;
/** 数组 a.length < size() ,则通过 Arrays.copyOf 复制 数组 r 返回。
* ps:不复制直接返回的话,就会把原集合的引用返回,不安全
* */
} 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;
}
/** 迭代器前 size() 个,赋给 数组r */
r[i] = (T)it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
2. AbstractList
AbstractList 继承于 AbstractCollection ,并且实现了 List 接口。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
3. AbstractDeque
+ _ + || 后面更新 ...
4. AbstractSet
+ _ + || 后面更新 ...
5. AbstractMap
6. 小结
(1) AbstractCollection: 大部分实现都很简单,迭代器遍历 + 具体操作即可。比较难理解的是 toArray方法,此方法考虑到并发的情况下使用,需要在每次获取迭代器元素时,判断迭代器元素个数 与 数组元素个数的关系,依此来决定是否进行扩容。