Java源码——AbstractCollection抽象类

宏观认识AbstractCollection抽象类

站在AbstractCollection的外面对它有一个整体的认识,正所谓:当局者迷,旁观者清。
在这里插入图片描述

抽象类官方注释(源码注释)

我开始看这个注释的时候有些地方也一脸懵,但是先看一下有个印象,再看后面的具体实现时会有恍然大悟的感觉。先来看一下官方注释怎么解释这个抽象类的,后面再一步一步地详细解释官方注释的真正含义。(这些官方注释可以再源码中看也可以再jdk8的在线文档中找到)。

在这里插入图片描述
先来看看简单翻译:

This class provides a skeletal implementation of the Collection interface, to minimize the effort required to implement this interface.
该类提供集合接口的框架实现,以最小化实现此接口所需的工作量。

To implement an unmodifiable collection, the programmer needs only to extend this class and provide implementations for the iterator and size methods. (The iterator returned by the iterator method must implement hasNext and next.)
为了实现一个不可修改的集合,程序员只需要扩展这个类并提供迭代器和size方法的实现。(iterator方法返回的迭代器必须实现hasNext和next。)

To implement a modifiable collection, the programmer must additionally override this class’s add method (which otherwise throws an UnsupportedOperationException), and the iterator returned by the iterator method must additionally implement its remove method.
为了实现一个可修改的集合,程序员必须另外重写这个类的add方法(否则会抛出不支持运行异常UnsupportedOperationException),迭代器方法返回的迭代器必须另外实现它的remove方法。

The programmer should generally provide a void (no argument) and Collection constructor, as per the recommendation in the Collection interface specification.
根据集合接口规范中的建议,程序员通常应该提供一个void(无参数)集合构造函数。

The documentation for each non-abstract method in this class describes its implementation in detail. Each of these methods may be overridden if the collection being implemented admits a more efficient implementation.
这个类中每个非抽象方法的文档详细描述了它的实现。如果想要实现一个更有效率更具能力的集合,或许可以重写这些方法。

This class is a member of the Java Collections Framework.
这个类是Java集合框架的一个成员。

public abstract class AbstractCollection<E> implements Collection<E>

AbstractCollection 的意思是抽象集合,它是Java集合框架的一个成员。它继承了Collection接口。

对比AbstractCollection和Collection的方法

从刚才的注释我们可以知道一点,AbstractCollection是集合Collection的基本实现,既然是基本实现,那么我们从方法上来看看怎么算是基本实现:
AbstractCollection 其中加粗表示提供了实现,– 表示当前接口/类中不存在这个方法。

Collection与AbstractCollection中的方法对比列表:

CollectionAbstractCollection
-protected AbstractCollection()
int size();int size();
boolean isEmpty();boolean isEmpty()
boolean contains(Object o);boolean contains(Object o)
Iterator< E > iterator();Iterator iterator()
Object[] toArray();Object[] toArray()
< T > T[] toArray(T[] a);< T > T[] toArray(T[] a);
boolean add(E e);boolean add(E e)
boolean remove(Object o);boolean remove(Object o)
boolean containsAll(Collection<?> c);boolean containsAll(Collection<?> c)
boolean addAll(Collection<? extends E> c);boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);boolean removeAll(Collection<?> c);
default boolean removeIf(Predicate<? super E> filter)-
boolean retainAll(Collection<?> c);boolean retainAll(Collection<?> c);
void clear();void clear();
boolean equals(Object o);-
int hashCode();-
default Spliterator spliterator()-
default Stream stream()-
default Stream parallelStream()-
-private static T[] finishToArray(T[] r, Iterator<?> it)
-private static int hugeCapacity(int minCapacity)
-String toString()

从上面的表中可以看出,AbstractCollection实现了部分方法,还新增了个别方法,这样就比较一目了然啦。还有一点,forEach方法,看过上一篇的应该明白我为什么要说到这个方法,它是Collection从Iterable继承来的。removeIf/spliterator/stream/parallelStream 这几个方法是父接口的default方法,所有AbstractCollection不用再写具体的实现方法。


微观认识AbstractCollection抽象类

接下来我们从细节来理解这个抽象类,以及理解官方注释的具体含义

集合的基本实现

AbstractCollectionch 抽象类实现了接口的大部分方法,除了Iterator以及size方法,该抽象实现类实现了 Collectionch 接口的基本实现,方便了子类的实现。

AbstractCollection()默认构造方法
/**
  * Sole constructor.  (For invocation by subclass constructors, typically
  * implicit.)
  * 唯一构造函数(用于子类构造函数调用,通常是隐式的)
  */
 protected AbstractCollection() {
    }

可以看到这个默认构造方法是protected 关键字修饰的,这个是用来给子类构造方法调用的。
对抽象类中protected构造器的理解:例子

  1. 抽象类不能实例化是绝对正确的,因此抽象类中不能包含public的构造方法;
  2. 抽象类的 protected构造方法会被子类隐式调用,也就是说并不一定在其子类的构造方法中显示调用super(),虽然对于AbstractCollection而言是建议这么做;
  3. 抽象类的 protected 构造方法可以用于初始化类中的某些属性,避免异常信息的出现;

在官方注释的第四段中也提到了:根据集合接口规范中的建议,程序员通常应该提供一个无参数的集合构造函数。子类的构造函数会隐式的调用父类的 protected 构造方法。

抽象方法
iterator()和size()
    /**
     * Returns an iterator over the elements contained in this collection.
     *
     * @return an iterator over the elements contained in this collection
     */
    public abstract Iterator<E> iterator();

    public abstract int size();

根据官方文档的第二段注释说明我们可以知道,子类只要实现这两个方法就可以轻松地实现一个不可修改的集合类(即只读集合类)。这保证了AbstractCollection的实现类只需要少量的工作,便可以将集合的功能基本实现。

实现一个只读集合的步骤:
  1. 继承AbstractCollection,创建一个内部类implements(实现)接口Iterator,并提供整个内部类的具体实现(包括方法remove(Object o),方法hasNext()和方法next()等)。
  2. 提供size()方法的实现,保证它始终返回一个恒定不变的值。

对于只读集合来说,它必然不可增删内部元素,也就是说,在最基本的集合实现类中,add,addAll,remove,removeAll,clear等方法是不起作用的。问中不是说其他方法都有具体实现吗,那为什么不起作用呢?接下来 我们从非抽象方法的源码中寻找答案。

非抽象方法

官方的第三段注释这样说:要实现一个可修改的集合类,必须另外重写这个类的add方法,迭代器方法返回的迭代器也必须另外实现它的remove方法。也就是说,默认的基本实现中add方法和内部类迭代器的remove方法是不可用的,从里一个角度来说,addAll是基于add实现的,而remove,removeAll,clear 等删除相关的函数是基于迭代器的删除方法实现的。

isEmpty()
    /**
     * {@inheritDoc}
     *
     * <p>This implementation returns <tt>size() == 0</tt>.
     */
    public boolean isEmpty() {
        return size() == 0;
    }

判空方法很简单,通过判断size()函数的返回值是否为 0。

contains(Object o)
    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over the elements in the collection,
     * checking each element in turn for equality with the specified element.
     *
     * @throws ClassCastException   {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    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;
    }

通过迭代器遍历链表,判断集合中是否包含某一元素。对于特殊对象 null ,采用 == 运算符判断,对于普通对象则调用 equals() 函数判断。

小问题:这里的equals()方法应该不是Collection接口中的,因为AbstractCollection没有实现equals()方法,另外官方说实现一个简单的集合最少的操作就是实现size和迭代器,并没有说一定要重写equals()方法,这个问题暂时还没有想通,希望能有大佬执教一下。

Object[] toArray()
    /**
     * {@inheritDoc}
     *
     * <p>This implementation returns an array containing all the elements
     * returned by this collection's iterator, in the same order, stored in
     * consecutive elements of the array, starting with index {@code 0}.
     * The length of the returned array is equal to the number of elements
     * returned by the iterator, even if the size of this collection changes
     * during iteration, as might happen if the collection permits
     * concurrent modification during iteration.  The {@code size} method is
     * called only as an optimization hint; the correct result is returned
     * even if the iterator returns a different number of elements.
     *
     * <p>This method is equivalent to:
     *
     *  <pre> {@code
     * List<E> list = new ArrayList<E>(size());
     * for (E e : this)
     *     list.add(e);
     * return list.toArray();
     * }</pre>
     */
    public Object[] toArray() {
        // Estimate size of array; be prepared to see more or fewer elements
        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;
    }

英文注释的大概意思可分为两层:

  1. 该实现方法返回一个数组,数组中的元素由迭代器遍历过程中返回的所有元素,其顺序与遍历顺序一致,数组索引从0开始。返回数组长度等于迭代器返回的元素总数。
  2. 迭代器在迭代过程中,即使集合的大小发生了变化(比如集合运行迭代时并发修改),也能返回正确的结果。这里size方法的调用仅仅为了优化。

该方法的算法很简单:
(1)先创建一个数组,大小为集合中元素的数量(通过size方法获取,这仅仅作为一个预期的数组大小)。
(2)通过迭代器遍历集合,将当前集合中的元素复制到数组中(复制引用)(对应语句:r[i] = it.next();)。
(3)如果集合中元素比预期的少,则调用Arrays.copyOf()方法将数组前面有元素的一部分复制并返回。
(4)如果集合中元素比预期的多,则调用finishToArray方法扩展数组,生成新数组并返回,否则返回(1)中创建的数组。

< T > T[ ] toArray(T[ ] a)
    /**
     *.........
     * List<E> list = new ArrayList<E>(size());
     * for (E e : this)
     *     list.add(e);
     * return list.toArray(a);
     * }</pre>
     *
     * @throws ArrayStoreException  {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    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;
    }

该方法的功能为通过泛型约束返回指定类型的数组,它的英文注释与上一个几乎一个意思,随意没有在文章了写出来,这里我们更专注与它的具体实现。在看源码之前,我一直不太会用这个方法,因为搞不懂为什么要给它传一个(数组)参数。通过源码可以看出,参数 T[] a 的主要作用其实是用来获取数据类型的。
在这里插入图片描述
不难看出,其实此方法中也有一个预期数组大小的概念,就是 r.length ,如果超出这个范围还有元素,同样会调用数组扩容方法 finishToArray(T[ ] r, Iterator<?> it)。

该方法的算法很简单:

  1. 如果传入数组的长度的长度大于等于集合的长度,则将当前集合的元素复制到传入的数组中
  2. 如果传入数组的长度小于集合的大小,则将创建一个新的数组来进行集合元素的存储
数组扩容:< T > T[ ] finishToArray(T[ ] r, Iterator<?> it)
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Reallocates the array being used within toArray when the iterator
     * returned more elements than expected, and finishes filling it from
     * the iterator.
     *
     * @param r the array, replete with previously stored elements
     * @param it the in-progress iterator over this collection
     * @return array containing the elements in the given array, plus any
     *         further elements returned by the iterator, trimmed to size
     */
    @SuppressWarnings("unchecked")
    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);
    }

看到这里我才发现,AbstractCollection有一个私有静态不可变变量 MAX_ARRAY_SIZE它表示可分配的最大数组大小。它初始化为Integer.MAX_VALUE-8,官方解释是某些操作系统需要预留部分空间给创建数组时产生的额外开销(及代码中的英文注释)。

finishToArray函数中使用的迭代器是上层方法toArray()中传过来的,此前以及迭代过一部分元素了,所有这里不需要从头开始(及代码中的英文注释)。

数组扩容的算法也比较简单:

  1. 当数组索引指向最后一个元素+1时,对数组进行扩容:即创建一个更长的数组,然后将原数组的内容复制到新数组中
  2. 扩容大小:cap + cap/2 +1(左移一位的意思就是除以2)
  3. 扩容前需要先判断是否数组长度是否溢出
数组容量:static int hugeCapacity(int minCapacity)
    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;
    }

这段代码可能太简单了,官方都没有为这个方法写注释,啊哈哈哈。
可以主要到的是,finishToArray和hugeCapacity都是私有方法。

add(E e)方法
    /**
     * {@inheritDoc}
     *
     * <p>This implementation always throws an
     * <tt>UnsupportedOperationException</tt>.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     * @throws IllegalArgumentException      {@inheritDoc}
     * @throws IllegalStateException         {@inheritDoc}
     */
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }

虽然有方法体,其实等于没有实现,默认抛出UnsupportedOperationException异常。到这里解答了一个疑惑,要创建一个可修改的集合类,必须要覆盖add(E e)方法,根据当前集合类的数据结构来添加元素。

remove(Object o) 方法
    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over the collection looking for the
     * specified element.  If it finds the element, it removes the element
     * from the collection using the iterator's remove method.
     *
     * <p>Note that this implementation throws an
     * <tt>UnsupportedOperationException</tt> if the iterator returned by this
     * collection's iterator method does not implement the <tt>remove</tt>
     * method and this collection contains the specified object.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     */
    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;
    }

官方的英文注释含义是:该实现遍历集合,找到指定的元素,并用迭代器的remove方法删除这个元素。也就是说,AbstractCollection的remove方法底层是又迭代器的remove方法实现的。到这里又解开了一个疑惑,创建可修改的集合类,实现的迭代器必须重新实现他的remove方法,因为迭代器的默认remove方法是不能用的。
迭代器接口的默认remove方法是:
default void remove() { throw new UnsupportedOperationException("remove"); }

接下来的方法都很简单了,都是一些批量操作,这些功能本质上都是通过上面的方法实现的。

批量操作函数:containsAll,addAll,remove,retainAll,clear
    /**
     * 遍历参数集合,依次将参数集合中的元素添加当前集合中
	 */
    public boolean containsAll(Collection<?> c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }
	/**
     * 遍历参数集合,依次判断参数集合中的元素是否在当前集合中,
     * 只要有一个不存在,则返回false,
     * 如果参数集合中所有的元素都在当前集合中存在,则返回true
	 */
    public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
    /**
     * 移除参数集合的元素
     * (1)获取当前集合的迭代器进行遍历
     * (2)如果当前集合中的元素包含在参数集合中,则删除当前集合中的元素
     *  注:返回值表示当前集合是否发生了变化
     */
    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;
    }
    /**
     * 求参数集合与当前集合的交集
     * (1)获取当前集合的迭代器进行遍历
     * (2)如果当前集合中的元素不在参数集合中,则将其移除。
     *  注:返回值表示当前集合是否发生了变化(如果当前集合是参数集合中的子集,当前集合就不发生变化)
     */
    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;
    }
    //删除所有元素
    public void clear() {
        Iterator<E> it = iterator();
        while (it.hasNext()) {
            it.next();
            it.remove();
        }
    }
toString() 方法
    /**
     * Returns a string representation of this collection.  The string
     * representation consists of a list of the collection's elements in the
     * order they are returned by its iterator, enclosed in square brackets
     * (<tt>"[]"</tt>).  Adjacent elements are separated by the characters
     * <tt>", "</tt> (comma and space).  Elements are converted to strings as
     * by {@link String#valueOf(Object)}.
     *
     * @return a string representation of this collection
     */
    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(' ');
        }
    }

也没什么特别的,就是把集合的所有元素拼接显示出来。

实现一个可修改集合类的步骤:
  1. 继承抽象类AbstractCollection并提供add(E e)方法的实现,根据当前集合类的数据结构来添加元素(数据结构有数组、链表、栈、队列等等)。
  2. 继承抽象类AbstractCollection并提供remove(Object o)方法的实现,根据当前集合类的数据结构来删除元素(数据结构有数组、链表、栈、队列等等)。
  3. 因为删除元素涉及到迭代器类的删除方法,所以还需要创建一个类implements接口Iterator,并提供整个类的实现(包括方法remove(Object o),方法hasNext()和方法next()等)。
  4. 继承抽象类AbstractCollection并提供size()方法的实现,保证它始终返回当前集合对象存储的元素个数。

集合框架全景图:
在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值