Java 集合类 源码分析学习----(2)集合基本接口的实现类

Java 集合类 源码分析学习

     —-(2)集合基本接口的实现类(AbstractCollection、AbstractList、AbstractDeque、AbstractMap、AbstractMap)

写在最全面:
     直接上图:AbstractCollection类是唯一一个直接实现Collection接口的子类,是几乎所有java数据结构类的父类,提供基本的集合操作,以供子类复用。AbstractList、AbstractDeque、AbstractMap、AbstractMap同理,提供对应数据容器的基本操作,以供子类调用。

《Java 集合类 源码分析学习----(2)集合基本接口的实现类》

1. AbstractCollection

AbstractCollection类实现了contains、toArray、remove()等方法;
未实现方法( 2个抽象方法 和 1个add()个方法直接抛出异常需要重写 ):

    public abstract Iterator<E> iterator();
    public abstract int size();
    //直接调用将抛出 ---- 不支持操作异常
    public boolean add(E e) {
      throw new UnsupportedOperationException();
   }

已实现的方法包括以下几个大类:

(1)增加(add、addAll)

add(E e)方法:需要子类重写,直接调用将会抛出异常。

    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }

addAll(Collection<? extends E> c) 方法:将添加外部集合c中的所有元素到此集合中,基本思想就是:遍历外部集合 c 的所有元素,逐个调用add(E e)添加到本集合中。显然,子类没有实现 add(E e) 方法,调用此方法也会抛出异常UnsupportedOperationException。

    public boolean addAll(Collection<? extends E> c) {
      	// 是否添加到本集合标志位,只要有一个元素添加成功,modified = true
        boolean modified = false;
        // 遍历集合 调用add方法
        for (E e : c) {
            if (add(e)) {
                modified = true;
            }
        }
        return modified;
    }

(2)删除(remove、removeAll、clear、retainAll)

remove(Object o)方法:基本思想就是,获取本集合的迭代器 –> 遍历迭代器 –> 找到与对象 o 相等的元素 –> 调用迭代器的remove() 方法移除此元素。
在判断元素相等时,分为null和非null两种情况:为null时,直接 = = 判断;非null时,调用equals方法。
源码中可以看出,remove(Object o) 方法对于集合中存在的多个与对象 o 相等的元素,只移除其中中第一个。
PS:迭代器的遍历之前已经提到过,具体使用见 Java 集合类 源码分析学习 —-(1)总体认识 中的2.1章节。

    public boolean remove(Object o) {
        Iterator<E> it = iterator();
        //  对象o为null的情况
        if (o==null) {
        	// 遍历迭代器
            while (it.hasNext()) {
                if (it.next()==null) {
                	// 调用迭代器的remove() 方法
                    it.remove();
                    // 只删除完第一个,立刻返回
                    return true;
                }
            }
        } else {
            while (it.hasNext()) {
                if (o.equals(it.next())) {
                    it.remove();
                    return true;
                }
            }
        }
        return false;
    }

removeAll(Collection<?> c) 方法,移除与外部集合 c 中相等的元素:与add和addAll 方法相类似,获取迭代器,遍历,组个调用remove方法,不多赘述,直接看代码:

    public boolean removeAll(Collection<?> c) {
    	// 外部集合 c 判空
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        //  遍历本集合
        while (it.hasNext()) {
        	// 元素存在于外部集合 c 中,直接从迭代器中移除
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }

retainAll(Collection<?> c) 方法,与removeAll 相反,保留与外部集合 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;
    }

clear() 方法,删除集合中所有元素:获取迭代器 –> 遍历 –> 逐个删除。

    public void clear() {
        Iterator<E> it = iterator();
        while (it.hasNext()) {
            it.next();
            it.remove();
        }
    }

(3)查询(contains、containsAll、iterator、size、isEmpty)

contains(Object o)、containsAll(Collection<?> c) : == 对比 remove(Object o)、removeAll(Collection<?> c) 方法 == ,直接上代码 + 注释:

public boolean contains(Object o) {
        /**  获取迭代器  */
        Iterator<E> it = iterator();
        /**  o 为 null 值时,判断 o == null
         *   o 不为 null 值时,判断 o.equals(it.next())
         *   利用迭代器的 hasNext + while 遍历
         * */
        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;
    }
    public boolean containsAll(Collection<?> c) {
        for (Object e : c) {
        	//  只要有一个 外部集合的元素 不包含在 此集合中,就 return false
            if (!contains(e)) {
                return false;
            }
        }
        return true;
    }

iterator() 和 size() 方法,为迭代器和集合大小方法,最开始已经提到为抽象方法 :

    public abstract Iterator<E> iterator();
    public abstract int size();

isEmpty() 方法,判断集合是否为空:直接判断 size() 是否为 0即可

    public boolean isEmpty() {
        return size() == 0;
    }

(4)转数组(toArray()、toArray(T[] a))

toArray() 方法,按集合中的顺序返回一个包含集合中所有元素的数组:
源码中,调用了 Arrays.copyOf 方法,复制集合中内容,所以修改数组中的内容,不会改变集合中的元素。
具体过程见 代码 + 注释:

/ **
* 此处明明初始化了一个与集合的大小相等的数组,却在之后还要判断迭代器的内容 ?
* 考虑到多线程情况下,迭代器内容发生改变
* /
  public Object[] toArray() {
        /**  初始化一个数组,大小 = 集合的大小  */
        Object[] r = new Object[size()];
        /**  获取集合的迭代器  */
        Iterator<E> it = iterator();
          /** 遍历迭代器  */
        for (int i = 0; i < r.length; i++) {
            /**当 迭代器内容 < 初始化数组大小 时, 
             * 调用Arrays.copyOf(),
             * 复制到新数组中,返回  */
            if (! it.hasNext()) {
                return Arrays.copyOf(r, i);
            }
            r[i] = it.next();
        }
            /**当 迭代器内容 >初始化数组大小 时, 
             * 调用 finishToArray(r, it)扩容数组并复制,
             * 复制到新数组中,返回  */
        return it.hasNext() ? finishToArray(r, it) : r;
    }

其中用到的 finishToArray 方法:
每次到达数组最大容量是进行扩容,扩容大小为:cap + cap * 2 + 1

    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);
    }

    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;
    }

toArray(T[] a) 方法, 同样是返回一个包含集合中所有元素的数组:如果数组 a 大小大于集合大小,则返回其中;否则重新分配内存空间,将新内存的引用交给 a 返回。

    public <T> T[] toArray(T[] a) {
        /**  集合大小  */
        int size = size();
        /**  数组 a 的长度 小于 集合的大小 : 利用反射 new 一个大小为 size 为数组,引用赋给 r
            数组 a 的长度 大于等于 集合的大小 : a 的引用 赋给 r
           */
        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 也就是,a.length >= size() ,数组大小 大于 集合大小  */
                if (a == r) {
                    /**  将剩下的数组中 size - (a.length - 1) 的元素赋值 null  */
                    r[i] = null;
                  /**  数组 a.length < size() ,则通过 Arrays.copyOf 复制 数组 r 返回。
                   * ps:不复制直接返回的话,就会把原集合的引用返回,不安全
                   * */
                } 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;
            }
            /**  迭代器前 size() 个,赋给 数组r   */
            r[i] = (T)it.next();
        }
        return it.hasNext() ? finishToArray(r, it) : r;
    }

2. AbstractList

AbstractList 继承于 AbstractCollection ,并且实现了 List 接口。

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>

3. AbstractDeque

			 + _ + ||  后面更新 ...

4. AbstractSet

			 + _ + ||  后面更新 ...

5. AbstractMap

6. 小结

(1) AbstractCollection: 大部分实现都很简单,迭代器遍历 + 具体操作即可。比较难理解的是 toArray方法,此方法考虑到并发的情况下使用,需要在每次获取迭代器元素时,判断迭代器元素个数 与 数组元素个数的关系,依此来决定是否进行扩容。

7. 参考博客

(1)https://www.cnblogs.com/android-blogs/p/5566212.html

    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/qq_17832777/article/details/84137147
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞