集合3-AbstractCollection源码分析

Abstract类实现了接口的大部分方法,除了Iterator以及size方法,抽象实现类实现了接口的默认实现,方便了子类的实现,接口的适配器模式

AbstractCollection

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

public Object[] toArray():创建size大小的Object数组,通过迭代器遍历数组大小范围的集合元素将其元素引用复制到数组当中,判断集合的元素是否大于小于等于预期大小(size的大小)分不同的处理情况来处理。
public <T> T[] toArray(T[] a):如果参数数组的大小size,则用反射创建一个T类型的数组,否则就是用这个数组的引用,还是通过迭代器遍历数组范围的集合元素将其元素引用复制到数组当中,判断集合的元素是否大于小于等于预期大小(size的大小)分不同的处理情况来处理。

// (1) 如果参数数组长度大于等于集合的长度,则将当前集合的元素复制到参数数组当中
// (2) 如果参数数组的长度小于集合的长度,则通过数组的反射创建T类型的新数组来容纳集合元素

 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) { //如果a 和 r 所引用的数组相同,则将剩下的数组元素都赋值为null,作为集合元素的结束标志
                    r[i] = null; // null-terminate
                } else if (a.length < i) {//如果参数数组的长度小于集合的长度,也就是r是通过反射新创建的数组,则通过Arrays.copyOf方法截短返回
                    return Arrays.copyOf(r, i);
                } else {//r是通过反射新创建的数组,但是参数数组长度大于集合长度,
                          //即a的长度大于新创建的r数组的长度(这在单线程情况下不可能发生,但是在
                          //并发情况下参数数组的长度可能发生变化),则将r数组复制到a数组当中,并
                          //将参数数组多的元素设置为null
                    System.arraycopy(r, 0, a, 0, i);//将创建的r数组所有元素复制到a数组当中
                    if (a.length > i) {
                        a[i] = null;
                    }
                }
                return a;
            }
            r[i] = (T)it.next(); //类型转换,可能会抛出ClassCastException异常
        }
        // more elements than expected
        // 如果集合长度大于数组的长度则拓展数组r,否则(集合长度正好等于数组的长度)直接返回数组r
        return it.hasNext() ? finishToArray(r, it) : r;
    }

private static <T> T[] finishToArray(T[] r, Iterator<?> it) 数组扩容的私有实现方法

 /**
     *  (1)对数组进行扩容:即创建一个更长的数组,然后将原数组的内容复制到新数组中
     *  (2)扩容大小:cap + cap/2 +1
     *  (3)扩容前需要先判断是否数组长度是否溢出
     */
    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; //扩容为 cap + cap/2 +1
                // overflow-conscious code
                if (newCap - MAX_ARRAY_SIZE > 0)//新扩大的容量发生溢出
                    newCap = hugeCapacity(cap + 1);
                //Arrays.copyOf方法内部创建了一个新的长度为newCap数组,
                //并将元素复制到这个新的数组当中,底层调用的是System.arraycopy native方法
                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;
    }

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

数组的最大可分配大小为整数的最大值,但是有些虚拟机会保留几个字节大小的空间存储头部,所以为了保险起见最大可分配的数组大小为整数最大值-8,否则会抛出内存溢出错误

 /**
     * 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;

public boolean contains(Object o) **
** public boolean remove(Object o)

都是通过迭代器进行遍历集合元素,对参数为null单独处理,如果下一个元素的值为null执行相对应的操作(集合允许null元素)

批量操作是通过对参数集合进行遍历,并执行相关的操作

public boolean containsAll(Collection<?> c)、public boolean addAll(Collection<? extends E> c)、public boolean removeAll(Collection<?> c)、public boolean retainAll(Collection<?> c)
初始化一个modified标志,对于批量操作只要有一个参数集合的元素被操作成功并使得集合发生改变则返回true,而不是所有参数集合都被成功操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,158评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,600评论 1 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,785评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,655评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,075评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,002评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,146评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,918评论 0 211
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,671评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,838评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,318评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,636评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,343评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,187评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,982评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,126评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,934评论 2 279

推荐阅读更多精彩内容