Collection接口
public interface Collection<E> extends Iterable<E> {}
它继承了Iterable接口,那么这个接口有什么用呢?
Iterable接口表示可迭代接口,它会返回一个迭代器Iterator。通过这个迭代器,我们可以遍历集合中元素了。
回到Collection接口,我们想一下,作为一个集合顶层接口,它应该包含哪些方法呢?想到那个对数据操作最经典的话,增删改查。
1、添加元素的方法
boolean add(E e);
boolean addAll(Collection<? extends E> c);
2、删除元素的方法
boolean remove(Object o);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
3、改这个操作在Collection接口没有,因为它没有提供索引,没办法更新对应索引的值。
4、查这个操作在Collection接口,集合中是否包含某些元素,以及返回迭代器遍历集合
boolean contains(Object o);
boolean containsAll(Collection<?> c);
Iterator<E> iterator();
5、其他重要方法
int size();
boolean isEmpty();
Object[] toArray();
<T> T[] toArray(T[] a);
抽象类AbstractCollection
AbstractCollection抽象类实现了Collection接口大部分方法,只剩下iterator()和size()方法需要子类复写,也就是说需要子类提供集合元素。
1、添加元素
public boolean add(E e) {
throw new UnsupportedOperationException();
}
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
add(E e) 方法直接抛出异常,表示AbstractCollection默认是不可修改的集合,子类必须复写这个方法,才能向集合中添加元素。
addAll(Collection<? extends E> c) ,通过for循环,将c集合中每个元素都添加到本集合中。
高级for循环这种格式,默认就是使用迭代器实现的,也就是说只要是Iterable 接口子类,都可以使用高级for循环。
2、删除元素
public boolean remove(Object o) {
Iterator<E> it = iterator();
// 注意这里,集合中有很多地方都是以o是否为空,分成两部分计算,虽然代码逻辑几乎一样。
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;
}
删除单个元素,通过迭代器iterator,遍历集合中元素,如果与o相等,就通过迭代器的remove方法,将它删除。
注意这里会根据被删除元素o是否为空,分成两部分,虽然两部分代码逻辑几乎一模一样。如果设计成在循环中进行两个条件判断,这样也可以,但是会增加比较次数。
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;
}
这个方法和removeAll方法正好相反,遍历本集合中每个元素,如果c集合中不包含这个元素,那么就删除这个元素。
public void clear() {
Iterator<E> it = iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
}
删除集合中全部元素。遍历集合中每个元素,得到一个删除一个,最终删除全部元素。
3、查找元素
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;
}
遍历每个元素,如果有和o对象相等的,就直接返回true。如果遍历全部元素,还是没有找到相等元素,就返回false。
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
是否包含c集合中全部元素。
4、重要方法
public abstract Iterator<E> iterator();
public abstract int size();
这两个方法等待子类复写。
public boolean isEmpty() {
return size() == 0;
}
通过size()方法判断集合是否为空。
public Object[] toArray() {
// 将terator迭代器中的元素全部转换成Object[],我们必须知道集合元素的大小。
// 但是size()返回值并不是集合元素的数量,所以要分情况
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
// 迭代器已经遍历完成,说明集合元素数量就是i。用Arrays.copyOf(r, i)就可以返回i长度的Object数组了
if (! it.hasNext())
return Arrays.copyOf(r, i);
r[i] = it.next();
}
// 如果it.hasNext()为true,表示集合中还有元素,那么就要扩大r数组,以便将剩余元素都存放进去。
// 如果为false,那么表示集合数量就是size(),直接返回这个r数组。
return it.hasNext() ? finishToArray(r, it) : r;
}
将集合转成Object[]数组,这个方法很重要。要转成数组,那么必须知道数组的大小。
这里需要注意的是size()返回值有可能和集合中元素数量不一样,因为强制约束它们两个值是相同的,要考虑到这种情况。 所以这里我们先创建一个size()长度的Object[],然后遍历迭代器,将值存入数组对应下标位置。然后就有三种情况:- 迭代器先遍历完成,就说明集合元素数量小于size()大小,而集合元素数量就是这个i, 然后通过Arrays.copyOf(r, i)方法,返回i长度的数组。
- 迭代器和r数组一起遍历完成,那么集合元素长度就是size(),直接返回这个r数组。
- 迭代器还没有遍历完成,那么就要对r数组进行扩容,存放剩余的集合元素,通过finishToArray(r, it)方法实现。
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;
}
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
int i = r.length;
while (it.hasNext()) {
int cap = r.length;
// 如果i == cap为true,表示数组已经满了,需要扩充。
if (i == cap) {
// 将数组长度扩充0.5倍。 (cap >> 1) 相当于 cap / 2
int newCap = cap + (cap >> 1) + 1;
// 防止数组长度超出Integer最大值
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;
}
总结
Collection接口
Collection 接口继承自Iterable接口,Iterable接口表示一个可迭代接口,主要作用就是返回一个迭代器Iterator。
顶层迭代器Iterator有三个重要方法:
- boolean hasNext();
- E next();
- void remove()。
用来遍历和移除集合中的元素。
Collection 接口中重要方法:
- boolean add(E e); 向集合中添加一个元素
- boolean addAll(Collection<? extends E> c); 将集合c中元素都添加到集合中。
- boolean remove(Object o); 从集合中删除o元素,也有可能删除不成功
- boolean removeAll(Collection<?> c); 从集合中删除集合c中包含的元素
- boolean retainAll(Collection<?> c); 只保留c集合中包含的元素
- void clear(); 清除集合中所有元素
- boolean contains(Object o); 判断集合中是否包含元素o
- boolean containsAll(Collection<?> c); 判断集合是否完整包含集合c中全部元素
- Iterator<E> iterator(); 返回迭代器
- int size(); 返回集合数量
- boolean isEmpty(); 判断集合是否为空
- Object[] toArray(); 将集合中元素转换成数组
- <T> T[] toArray(T[] a); 将集合中元素转换成T类型的数组
抽象类AbstractCollection
- 强制子类复写Iterator<E> iterator() 和int size() 方法
- boolean add(E e) 方法直接抛出异常,如果不复写这个方法,那么集合是不能添加数据的。
- boolean addAll(Collection<? extends E> c) 方法,是遍历集合,然后调用add方法,将元素添加到集合中,所以也会抛出异常。
- remove(Object o) 方法,是调用迭代器的remove()实现的。
- removeAll(Collection<?> c)方法,是通过迭代器遍历集合,然后使用集合c的contains方法,判断元素在集合中是否也存在,是的话就调用迭代器的remove()方法,删除这个元素。
- retainAll(Collection<?> c)方法,与removeAll方法流程大体一样,只不过它是判断集合c中不包含就调用迭代器的remove()方法,删除这个元素。
- void clear(); 通过迭代器遍历集合,然后调用r迭代器的remove()方法,删除每个元素
- boolean contains(Object o); 通过迭代器遍历集合,查看是否有与o相等的元素。
- boolean containsAll(Collection<?> c); 遍历集合c,拿集合c中每个元素调用本集合的contains方法。如果有返回false,那么containsAll方法就直接返回false。只有全部通过才返回true。
- Object[] toArray(); 和<T> T[] toArray(T[] a); 通过迭代器将集合转化成对应数组。