Java 集合框架系列一:JDK 1.8 Collection 和 AbstractCollection 详解

Collection 继承关系

public interface Collection<E> extends Iterable<E> {
}    

Collection 是在 JDK 1.2 提供的接口,它继承了 Iterable 接口,说明 Collection 的所有实现类都支持 for-each 循环。

类文档解读

老规矩,先通过 Collection 的类文档了解一下能得到哪些信息。

  • Collection 是 Java 集合框架的根接口,集合是指用来存储一组对象的容器,集合中的每个对象都是一个元素。
  • 有些集合允许元素重复,有些不允许,有些集合元素有序,有些无序。JDK 没有对此接口进行实现而是对其更具体的子接口进行了实现,如 List 和 Set。此接口一般当作方法参数用来传递集合,如 addAll(Collection c),这种方法你一定是见过的。
  • 对于此接口的实现类,一般建议提供两个构造器,一个是无参构造器,创建一个空集合,一个是接受 Collection 类型入参的构造器。这不是一个强制约定,但是 JDK 实现类都遵从了这个约定。
  • 如果对集合进行了不支持的操作,必要的话需要抛出 UnsupportedOperationException 异常。
  • 有些集合对元素有限制,比如元素不能是 null,有些还对元素类型有限制,尝试添加不合格的元素可能会抛出 NullPointerException 或者 ClassCastException 异常,也有可能会返回 false,要看实现类的具体实现。

总结一下,Collection 就是对集合框架的一个基础规范与抽象,提供了集合类应该具备的特征让实现类根据自身的特性进行实现。

Collection API

在阅读源码前,我们可以先自行想象一下,如果我们想封装下数组或链表以方便操作,我们需要封装哪些功能呢?比如:统计大小、插入或删除数据、清空、是否包含某条数据等等。而 Collection 就是对这些常用操作进行提取,只是其很全面、很通用。下面我们看看它都提供了哪些方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MTg6dxZ-1599539641319)(G:\学习记录\Java 集合框架\images\微信截图_20200829172211.png)]
接下来对这些方法进行解读。

public interface Collection<E> extends Iterable<E> {
    // Query Operations

    /**
     * 返回集合中元素的数量,如果元素数量超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE
     */
    int size();

    /**
     * 如果集合中没有元素返回 true
     */
    boolean isEmpty();

    /**
     * 如果集合中包含指定的元素则返回 true,如果集合中包含 null 元素并且要查找的元素也是 null 的时候也返回 true。
     * 
     * @throws 如果指定的元素与集合元素类型不兼容则抛出 ClassCastException 
     * @throws 如果集合不允许 null 元素且指定的元素是 null 则抛出 NullPointerException
     */
    boolean contains(Object o);

    /**
     * 返回此集合中元素的迭代器。对于元素返回的顺序没有任何保证(除非此集合是某个提供保证的类的实例)。
     */
    Iterator<E> iterator();

    /**
     * 返回包含此集合中所有元素的数组。如果此集合对迭代器返回其元素的顺序作出任何保证,则此方法必须
     * 以相同的顺序返回元素。返回的数组是“安全的”,调用者可以自由修改返回的数组。
     */
    Object[] toArray();

    /**
     * 返回包含集合中所有元素的数组,返回的数组运行时类型是指定的数组的运行时类型,
     * 如果指定的数组可以容纳集合的所有元素,则将集合的所有元素填充到指定数组内并返回,
     * 如果指定的数组不能够容纳指定的数组,则创建一个与集合容量相同的新数组并返回。
     * 如果指定的数组容量超过集合的容量,则剩余的元素使用 null 填充。
     * 如果此集合对迭代器返回其元素的顺序作出任何保证,则此方法必须以相同的顺序返回元素。
     * 
     * 与 toArray() 相比,此方法允许对输出数组的运行时类型进行精确控制,并且在某些情况下可用于节省分配成本。
     * 假设 x 是已知只包含字符串的集合。以下代码可用于将集合转储到新分配的字符串数组 y 中:
     * 
     * String[] y = x.toArray(new String[0]);
     *
     * 注意: toArray(new Object[0]) 和 toArray() 的效果相同。
     *
     * @throws 指定数组的运行时类型不是此集合中元素的运行时类型的父类型则抛出 ArrayStoreException 
     * @throws 如果指定的数组是 null 则抛出 NullPointerException
     */
    <T> T[] toArray(T[] a);

    // Modification Operations

    /**
     * 将指定元素添加到集合,如果通过此操作集合发生了改变返回 true,
     * 如果此集合不允许重复的元素并且此元素已存在则返回 false.
     * 集合可能对添加的元素存在限制,如不能为 null 或者元素的类型。
     * 如果集合拒绝添加某个特定元素,不是因为它已经包含该元素,那么它必须抛出异常(而不是返回false)
     * 
     * @throws 如果集合不支持此操作则抛出 UnsupportedOperationException
     * @throws 如果添加的元素不兼容集合元素的类型则抛出 ClassCastException
     * @throws 如果添加的元素是 null 且此集合不支持 null 元素则抛出 NullPointerException
     * @throws 如果元素的某些属性阻止将其添加到此集合中则抛出 IllegalArgumentException
     * @throws 如果此时由于插入限制而无法添加元素则抛出 IllegalStateException
     */
    boolean add(E e);

    /**
     * 移除集合中指定的元素,如果该元素存在返回 true
     *
     * @throws 如果移除的元素不兼容集合元素的类型则抛出 ClassCastException
     * @throws 如果移除的元素是 null 且此集合不支持 null 元素则抛出 NullPointerException
     * @throws 如果集合不支持此操作则抛出 UnsupportedOperationException
     */
    boolean remove(Object o);

    // Bulk Operations

    /**
     * 如果此集合中包含指定的集合中所有的元素返回 true
     *
     * @throws 如果指定的元素至少有一个不兼容集合元素的类型则抛出 ClassCastException
     * @throws 如果指定的元素存在 null 且此集合不支持 null 元素则抛出 NullPointerException
     */
    boolean containsAll(Collection<?> c);

    /**
     * 将指定集合中的所有元素添加到此集合,如果通过此操作集合发生了改变返回 true
     *
     * @throws 如果集合不支持此操作则抛出 UnsupportedOperationException
     * @throws 如果添加的元素不兼容集合元素的类型则抛出 ClassCastException
     * @throws 如果添加的元素是 null 且此集合不支持 null 元素则抛出 NullPointerException
     * @throws 如果元素的某些属性阻止将其添加到此集合中则抛出 IllegalArgumentException
     * @throws 如果此时由于插入限制而无法添加元素则抛出 IllegalStateException
     */
    boolean addAll(Collection<? extends E> c);

    /**
     * 移除此集合中所有包含在指定集合中的元素。此调用返回后,此集合将不包含与指定集合相同的元素。
     * 如果通过此操作集合发生了改变返回 true
     * 
     * @throws 如果集合不支持此操作则抛出 UnsupportedOperationException
     * @throws 如果移除的元素不兼容集合元素的类型则抛出 ClassCastException
     * @throws 如果移除的元素是 null 且此集合不支持 null 元素则抛出 NullPointerException
     */
    boolean removeAll(Collection<?> c);

    /**
     * 移除此集合中满足给定条件的所有元素。在迭代期间引发的错误或运行时异常被抛给调用方。
     *
     * 默认实现使用 Iterator 遍历集合的所有元素。使用 Iterator.remove() 删除每个匹配元素。 
     * 如果集合的 Iterator 不支持移除,则会在第一个匹配元素上引发 UnsupportedOperationException。
     * 
     * 如果删除了任意元素则返回 true 
     * @throws 如果指定的 filter 是 null 则抛出 NullPointerException
     * @throws 如果不能删除元素则抛出 UnsupportedOperationException
     */
    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;
    }

    /**
     * 仅保留此集合中包含在指定集合中的元素。换言之,从该集合中移除指定集合中未包含的所有元素。
     * 如果通过此操作集合发生了改变返回 true
     * 
     * @throws 如果不支持此操作则抛出 UnsupportedOperationException
     * @throws 如果指定的元素不兼容集合元素的类型则抛出 ClassCastException
     * @throws 如果指定的元素存在 null 且此集合不支持 null 元素则抛出 NullPointerException
     */
    boolean retainAll(Collection<?> c);

    /**
     * 删除此集合中的所有元素(可选操作)。
     *
     * @throws 如果不支持此操作则抛出 UnsupportedOperationException
     */
    void clear();

    // Comparison and hashing

    /**
     * 将指定的对象与此集合进行相等性比较
     */
    boolean equals(Object o);

    /**
     * 返回此集合的哈希码,应该注意需要重写对象的 equals 方法和 hashCode 方法,
     * c1.equals(c2) 意味着 c1.hashCode()==c2.hashCode()
     */
    int hashCode();

    /**
     * 重写 Iterable 接口的 spliterator() 方法
     */
    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }

    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

关于 toArray 方法现在存在一点点疑惑,来实践一下。

toArray 示例

public class CollectionDemo {

    public static void main(String[] args) {
        Collection<User> list = new ArrayList<User>();
        list.add(new User("xx"));
        list.add(new User("yy"));
        // User[] array = (User[]) list.toArray(); // 不能强转,否则运行时抛出异常
        Object[] array = list.toArray();
        array[0] = new User("zz");
        User user = (User)array[1];
        user.name = "11";
        System.out.println(list);// [User{name='xx'}, User{name='11'}]
        System.out.println(Arrays.toString(array));// [User{name='zz'}, User{name='11'}]
    }

    private static class User {
        private String name;

        public User(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}

结论:

  • toArray() 方法返回值必须是 Object[],不能进行强转,否则会抛出 ClassCastException 异常。
  • 增删数组元素不会影响原集合,但是如果修改数组内元素的属性,会影响原集合,因为数组和集合使用了相同的对象引用。

toArray(T[] a) 示例

	public static void main(String[] args) {
        Collection<String> strings = new ArrayList<String>();
        strings.add("x");
        strings.add("y");
        String[] strArray = strings.toArray(new String[3]);
        System.out.println(Arrays.toString(strArray)); // [x, y, null]
        String[] t1 = new String[2];
        String[] t2 = new String[1];
        System.out.println(t1 == strings.toArray(t1)); // true 说明使用的是传入的数组
        System.out.println(t2 == strings.toArray(t2)); // false 说明没有使用传入的数组而是新建的
    }

结论:

  • 如果指定数组容量超过了集合的容量,剩下的元素用 null 填充。
  • 如果指定的数组容量大于等于集合的容量,则直接填充指定的数组并返回。
  • 如果指定的数组容量小于集合的容量,会创建一个新的数组。

AbstractCollection 继承关系

AbstractCollection 是 JDK 1.2 提供的抽象类,实现了 Collection 接口,是对 Collection 接口中方法的部分实现,为了给 Collection 的实现类提供一个通用的实现,子类可以选择重写它实现的方法,在下图的方法列表中,Iterator() 和 size() 方法是抽象方法,其余方法都进行了通用的实现。
在这里插入图片描述

类文档解读

老规矩,先通过 AbstractCollection的类文档了解一下能得到哪些信息。

  • AbstractCollection 是 Collection 接口的最小化实现。
  • 如果实现一个不可变集合,只需要重写 AbstractCollection 的 iterator 和 size 方法,iterator 方法返回的迭代器必须重写 hasNext 和 next 方法。
  • 如果实现一个可修改的集合,必须另外重写 AbstractCollection 的 add 方法(否则会抛出 UnsupportedOperationException),iterator 方法返回的迭代器必须重写 remove 方法。
  • 如果正在实现的集合有更有效的实现,则可以重写这些方法中的每一个。

AbstractCollection API

接下来对这些方法进行解读。

public abstract class AbstractCollection<E> implements Collection<E> {
    /**
     * 提供给子类调用
     */
    protected AbstractCollection() {
    }

    // Query Operations

    // 返回当前集合的迭代器
    public abstract Iterator<E> iterator();
	// 返回集合中元素的数量,如果元素数量超过 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE
    public abstract int size();
    // size() 为0 表示空集合
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * 调用到了 it.next() 与 it.hasNext() 这也是为什么文档会写实现集合类需要重写 Iterator 的这两个方法。
     */
    public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
            	// null 判断
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
            	// 非 null 元素使用 equals 判断,所以建议重写元素的 equals 方法
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }

    public Object[] toArray() {
        // 创建一个 Object[] 存储集合元素
        Object[] r = new Object[size()];
        Iterator<E> it = iterator();
        for (int i = 0; i < r.length; i++) {
            // 如果集合元素变少了,可能存在并发删除的情况
            if (! it.hasNext()) 
                return Arrays.copyOf(r, i);
            r[i] = it.next();
        }
        // 如果集合元素变多了,并发增加,将进行扩容
        return it.hasNext() ? finishToArray(r, it) : r;
    }

    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        // 如果指定的数组 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()) { 
                // 如果 a == r,将r的每项值赋空,并将a返回
                if (a == r) {
                    r[i] = null; // null-terminate
                } else if (a.length < i) {
                    //如果a的长度小于r,直接调用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
            r[i] = (T)it.next();
        }
        // 判断是否遍历未结束,以防多线程操作的时候集合变得更大,进行扩容
        return it.hasNext() ? finishToArray(r, it) : r;
    }

    /**
     * 要分配的数组的最大大小。一些 vm 在数组对象头中保存额外信息,最多不超过 8 字节。
     * 尝试分配较大的数组可能会导致 OutOfMemoryError: 请求的数组大小超过 VM 限制
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 当迭代器返回的元素超过预期时,重新分配在 toArray 中使用的数组,并从迭代器中填充它。
     * 数组扩容
     */
    @SuppressWarnings("unchecked")
    private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
        int i = r.length;
        // 如果迭代器中还有元素
        while (it.hasNext()) {
            int cap = r.length;
            // 数组索引指向最后一个元素+1时
            if (i == cap) {
                // 创建一个大小为(cap + cap/2 +1)的数组,然后将原数组的内容复制到新数组中。1.5 倍扩容
                int newCap = cap + (cap >> 1) + 1;
                // 扩容前需要先判断是否数组长度是否溢出,即是否超过了集合默认的最大值 MAX_ARRAY_SIZE
                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;
    }

    // Modification Operations
	// 添加元素默认抛出 UnsupportedOperationException
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }

    /**
     * 通过迭代器删除第一个匹配的 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;
    }


    // Bulk Operations

    /**
     * for-each 循环调用 contains() 方法判断
     */
    public boolean containsAll(Collection<?> c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }

    /**
     * 如果没有重写 add 方法会抛出 UnsupportedOperationException
     */
    public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }

    /**
     * 通过 contains 方法判断是否包含指定集合中的元素,通过迭代器的 remove 方法删除元素
     * 如果没有实现迭代器的 remove 方法则会抛出 UnsupportedOperationException
     */
    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;
    }

    /**
     * 实现原理和 removeAll 相似
     */
    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;
    }

    /**
     * 通过迭代器的 remove 方法删除元素
     */
    public void clear() {
        Iterator<E> it = iterator();
        while (it.hasNext()) {
            it.next();
            it.remove();
        }
    }
    //  String conversion
    // 重写 toString 方法,使用 [] 包括整个集合,元素之间使用 , 分隔
    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值