AbstractCollection
本系列的Java源码版本是官网最新版java 11
在使用容器之前,首先需要熟悉一个概念 迭代器 ,不只是Java语言有这样的概念,其他的语言,比如python、C++等面向对象的语言中都有这些概念。迭代器是容器的基础
In computer programming, an iterator is an object that enables a programmer to traverse a container, particularly lists.
维基百科上意思:迭代器 就是能让编程者遍历容器的对象,特别是List
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;
}
从代码上看,可以发现容器2个特点
- 可以存放null对象
- 判断对象相等是通过对象的equals方法
这里有一个代码处理上的小细节,当 o != null
判断对象相等的时候 o.equals(it.next())
o对象放在前面,这样的好处是防止 NullPointerException
。在日常开发过程中经常会碰到这个问题,使用这个小技巧可以在一定程度上避免此类问题发生
代码示例
package com.jaksona;
public class Main {
public static void main(String[] args) {
// write your code here
MyUnmodifiedCollection c1 = new MyUnmodifiedCollection(1);
System.out.println(c1.contains(1));
// 输出 true 因为对象Integer重写了equals方法
MyUnmodifiedCollection c2 = new MyUnmodifiedCollection(new MyCustomInteger(1));
System.out.println(c2.contains(new MyCustomInteger(1)));
// 输出 false 因为对象MyCustomInteger未重写了equals方法
}
}
MyInteger
类里面就是存储了一个int成员变量,你会发现同样是数字1输出的结果是不一样的,原因就是 Integer
官方已经实现了 equlas
和 hashCode
方法。可能你会对前面的那两条语句有点困惑,数字1为啥变成了Integer,这个后续会跟大家说,这里涉及到一个java 1.5引入的概念 装箱(boxing)和拆箱(unboxing)
,因为集合里面都是存储对象,所有基本类型char、int、long等等都会自动包装成对应的引用对象
remove
remove方法跟contains基本一样,只是在返回值前增加了一行Iterator的remove方法调用
removeAll和retainAll
/**
* 与c互斥
*/
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;
}
/**
* 与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;
}
这俩方法是常用的集合与集合之间的运算
toArray
public Object[] toArray() {
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;
}
该方法有个特点,如果另一个线程在操作iterator(前提是支持多线程操作)也可以保证返回的数组是最新的,这个特性是在 finishToArray
中实现的
这里有一个优化细节处理,如果iterator元素个数少于size会直接返回copy数组
toArray
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;
}
该方法与上面的方法是重载的关系,返回指定类型的数组。日常开发过程中我们常常需要将一个List转换成数组,这个时候就可以使用这两个方法了。
总结
AbstractCollection实现了集合基本操作,比如contains、remove、containsAll、addAll,还有集合之间的操作removeAll、retainAll