简介
Collection继承自Iterable,Collection接口是Java集合两大分支中的一支,Queue、List、Set都是Collection的扩展;集合大类分为了Collection和Map。
常见的数据结构:数组(Array)、集(Set)、队列(Queue)、链表(Linkedlist)、树(Tree)、堆(Heap)、栈(Stack)和映射(Map)等结构。
Iterable接口
实现这个接口的类允许使用 for-each loop 语句来遍历
public interface Iterable<T> { // 返回一个迭代器对象 Iterator<T> iterator(); // JDK1.8 新增遍历方式 default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } // 可分割迭代器 default Spliterator<T> spliterator() { return Spliterators.spliteratorUnknownSize(iterator(), 0); } }
Iterable最早出现在JDK 1.5,开始只有iterator()一个抽象方法,需要子类来实现一个内部迭代器Iterator遍历元素。
后两个方法是Java 8后新添加的,forEach(Consumer action)是为了方便遍历操作集合内的元素,Spliterator(splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比最早Java提供的顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历。
Collection 接口
Collection是一个高度封装的集合接口,它提供了所有集合要实现的默认方法
public interface Collection<E> extends Iterable<E>
基本方法
// 返回此集合的元素数量 int size(); // 返回集合是否为空 boolean isEmpty(); // 如果包含元素O则返回为true boolean contains(Object o); // 返回此集合元素的迭代器 Iterator<E> iterator(); // 将此集合转化为数组 Object[] toArray(); // 将此集合转化为制定类型数组 <T> T[] toArray(T[] a); // 返回值是boolean,添加一个元素 boolean add(E e); // 删除指定的元素 boolean remove(Object o); // 如果包含集合C返回为true boolean containsAll(Collection<?> c); // 返回值是boolean类型,将集合C中的所有元素添加到此集合 boolean addAll(Collection<? extends E> c); // 删除包含集合c的所有元素 boolean removeAll(Collection<?> c); // 获取集合c与此集合的交集 boolean retainAll(Collection<?> c); // 删除此集合中的所有元素 void clear(); // 将指定的对象O与此集合进行比较 boolean equals(Object o); // 返回此集合的INT类型哈希码 int hashCode();
默认实现方法
// 删除满足条件的所有元素 default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; } @Override // 创建一个Spliterator default Spliterator<E> spliterator() { return Spliterators.spliterator(this, 0); } // 返回以此集合作为源的顺序流 default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } // 创建一个Spliterator并行流 default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); }
AbstractCollection 抽象类
AbstractCollection 实现Collection接口,该类是一个抽象类,提供了对集合类操作的一些基本实现。List和Set的具体实现类基本上都直接或间接的继承了该类
public abstract class AbstractCollection<E> implements Collection<E>
构造函数
protected AbstractCollection() { }
未实现的方法
// 获取迭代器 public abstract Iterator<E> iterator(); // 获取集合元素个数 public abstract int size();
已实现的方法
AbstractCollection所有已实现的方法子类都会覆盖掉,官方的说法 “此类提供集合的骨架实现接口,以最小化实现此接口所需的工作”
判空
public boolean isEmpty() { // 元素个数为0,就为空 return size() == 0; }
添加
public boolean add(E object) { throw new UnsupportedOperationException(); } public boolean addAll(Collection<? extends E> c) { // 是否添加成功 boolean modified = false; // 迭代参数中每一个元素 for (E e : c) // 添加元素并判断是否添加成功 if (add(e)) modified = true; // 返回是否添加成功 return modified; }
直接调用AbstractCollection的add方法会抛出异常,所以我们在实现Collection自定义数据结构时一定要覆盖此方法。
删除
public boolean remove(Object object) { //获取子类实现的 迭代器 Iterator<?> it = iterator(); if (object != null) { // 参数不为空 // 循环迭代 while (it.hasNext()) { // 对比找到元素 if (object.equals(it.next())) { // 移除 it.remove(); return true; } } } else { // 参数为空 // 循环迭代,参数为空相当于清空 while (it.hasNext()) { if (it.next() == null) { // 移除 it.remove(); return true; } } } return false; } 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; }
清空
public void clear() { // 获取子类实现的迭代器,挨个遍历,删除 Iterator<E> it = iterator(); // 循环迭代 while (it.hasNext()) { it.next(); // 单线程使用迭代器的 remove() 方法不会导致 fail-fast it.remove(); } }
包含
public boolean contains(Object o) { // 获取当前迭代器 Iterator<E> it = iterator(); if (o==null) { // 参数为空 // 迭代查找是否包含空元素 while (it.hasNext()) if (it.next()==null) // 包含空返回true return true; } else { // 迭代每一个元素进行比较 while (it.hasNext()) if (o.equals(it.next())) // 元素equals相等返回true return true; } return false; } public boolean containsAll(Collection<?> c) { // 遍历参数中每一个元素 for (Object e : c) // 有一个不包含就返回false if (!contains(e)) return false; // 能到这儿说明都包含 return true; }
取交集
public boolean retainAll(Collection<?> collection) { boolean result = false; Iterator<?> it = iterator(); // 迭代当前集合 while (it.hasNext()) { // 参数集合中不包含,就在当前集合中移除此元素 if (!collection.contains(it.next())) { // 移除 it.remove(); // 移除成功,结果置为true result = true; } } return result; }
转数组
public <T> T[] toArray(T[] a) { // 获取集合长度 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) { //如果数组是参数中的数组,则将剩余部分的值都设置为null r[i] = null; // null-terminate // 如果传入的数组长度小于集合长度 } else if (a.length < i) { // 通过Arrays.copyOf将之前数组中的元素复制到新数组中 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; // 起始位置 if (i == cap) { // 新数组扩容,新长度 = 原长度*2 + 1 int newCap = cap + (cap >> 1) + 1; // 新长度小于上限(int最大值-8) if (newCap - MAX_ARRAY_SIZE > 0) newCap = hugeCapacity(cap + 1); // 创建新数组并拷贝原数组 r = Arrays.copyOf(r, newCap); } // 下标加1继续放值 r[i++] = (T)it.next(); } // 新数组 return (i == r.length) ? r : Arrays.copyOf(r, i); } private static int hugeCapacity(int minCapacity) { // 长度小于0抛出越界异常 if (minCapacity < 0) // overflow throw new OutOfMemoryError ("Required array size too large"); // 长度超限默认最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
对于toArray方法可能很多人会有疑问,toArray方法第二步如果数组长度不够不是已经建了新数组吗,为什么在迭代过程中还要对新数组进行扩容?
toArray不是线程安全的,在迭代的过程中,没有人能保证数据结构中元素不会增加或减少,减少到没关系,增加后新创建的数组放不下了怎么办?所以只能做扩容处理。