Collection容器家族(ArrayList源码详解)

一.ArrayList在Colection集合体系中的位置及概述

        ArrayList理解起来很简单,Array数组 阵列的意思、List线性表 列表,加在一起就是数组去实现线性表的一个实现类。ArrayList是由AbstractList超类特化而来。在功能上实现了add, get, set, remove等与存储结构相关的方法,我们重点理解这些功能。

        ArrayList继承自AbstractList超类,实现list接口外、RandomAccess 随机访问接口,、Cloneable克隆元素接口、java.io.Serializable序列化。(public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable)

二、父类AbstractList已经实现List接口,为什么ArrayList类还要去实现List接口?

        ArrayList继承了AbstractList,实现了List接口。这里面存在着一个问题,就是AbstractList已经实现了List接口,那么我们ArrayList又继承了AbstractList为什么还要实现List接口呢?咱们根据下面的示例代码来看一下可能会存在的问题。

public class Day01Demo {
    /**
     * 交通工具接口
     */
    private interface Moveable {
        // 移动
        public abstract void move();

        // 输出信息
        public abstract void show();
    }

    /**
     * 交通工具抽象类
     */
    private abstract class AbstractMoveable implements Moveable {
        private String name = "";       // 交通工具名称
        private String clas = "";       // 种类
        private int custmerNum = 0;     // 满载人数

        public AbstractMoveable(String clas, int custmerNum, String name) {
            this.clas = clas;
            this.custmerNum = custmerNum;
            this.name = name;
        }

        public abstract void move() ;

        @Override
        public void show() {
            System.out.println("- 交通工具信息 - AbstractMoveable抽象类 -");
            System.out.println("名称:" + this.name);
            System.out.println("类型:" + this.clas);
            System.out.println("满载:" + this.custmerNum);
        }

        @Override
        public String toString() {
            return "AbstractMoveable{" +
                    "name='" + name + '\'' +
                    ", clas='" + clas + '\'' +
                    ", custmerNum=" + custmerNum +
                    '}';
        }
    }

    /**
     * 陆地工具---继承---交通工具超类---实现接口
     */
    private class LandMoveable extends AbstractMoveable implements Moveable {
        private String brandModel = "";     // 发动机型号

        public LandMoveable(String clas, int custmerNum, String name, String brandModel) {
            super(clas, custmerNum, name);
            this.brandModel = brandModel;
        }

        @Override
        public void move() {
            System.out.println("陆地工具的move方式 - LandMoveable");
            System.out.println(this.brandModel + " 发动机 1E65F 单缸 二行程");
        }

        @Override
        public void show() {
            super.show();
            System.out.println("交通工具信息 - FlyMoveable");
            System.out.println("发动机型号:"+this.brandModel);
        }

        @Override
        public String toString() {
            return super.toString() + "LandMoveable{" +
                    "brandModel='" + brandModel + '\'' +
                    '}';
        }
    }

    /**
     * 飞行工具---继承---交通工具超类
     */
    private class FlyMoveable extends AbstractMoveable {
        private String propellerModel = "";     // 推进器型号

        public FlyMoveable(String clas, int custmerNum, String name, String propellerModel) {
            super(clas, custmerNum, name);
            this.propellerModel = propellerModel;
        }

        @Override
        public void move() {
            System.out.println("飞行工具的move方式 - FlyMoveable");
            System.out.println(this.propellerModel + " 推进器 向后喷气");
        }

        @Override
        public void show() {
            super.show();
            System.out.println("交通工具信息 - FlyMoveable");
            System.out.println("推进器型号:"+this.propellerModel);
        }

        @Override
        public String toString() {
            return super.toString() + "FlyMoveable{" +
                    "propellerModel='" + propellerModel + '\'' +
                    '}';
        }
    }

    public <T> T createProxy(T obj) {
        return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("代理交通工具要调用 ->" + method.getName() + "<-方法,被代理的对象是-> " + obj.getClass().getSimpleName());
                        return method.invoke(obj, args);
                    }
                });
    }

    @Test
    public void runDemo() {
        Moveable landMoveable = new LandMoveable("陆地工具", 13,
                "奔驰", "M270发动机");
        Moveable flyMoveable = new FlyMoveable("飞行工具", 107,
                "播音747", "涡轮喷气");

        System.out.println("==========使用接口==========");
        landMoveable.move();
        landMoveable.show();
        System.out.println("===========================");
        flyMoveable.move();
        flyMoveable.show();
        System.out.println("===========================");
        System.out.println();

        System.out.println("==========使用代理==========");
        Moveable lm = createProxy(landMoveable);
        lm.move();
        lm.show();
        System.out.println("===========================");
        Moveable fm = createProxy(flyMoveable);
        fm.move();
        fm.show();
        System.out.println("===========================");
    }
}

运行结果:

        根据上面演示代码,交通工具接口Moveable作用是规定其实现类必须实现的几种方法,那么AbstractMoveable所起到的作用是什么?我们换个角度考虑一下如果没有AbstractMoveable超类,那么无论是飞行交通工具还是陆地交通工具不但要继承Moveable接口,这两个类还会有很多的重复代码,也就体现出抽象类存在的意义:提取出子类所共有的属性和行为特征。也就体现出AbstractMoveable超类减少代码重复,把交通工具最基本的实现都去实现,留下需要扩展的功能让子类去实现,AbstractMoveable起到了“模板类”和“基类”的作用。

最终总结如下:(为什么父类实现了的接口子类还要去实现?)

1.根据上面代码演示情况,在不使用接口的动态代理情况下,二者毫无区别,都可以运用接口去实现多态;但是如果涉及到使用接口的动态代理就必须显示的实现Moveable接口,否者程序将报错。

2.易理解性,这样的风格通俗易懂简明扼要。因为这种显示的实现的规则是有意向的,我们无需翻看它到底是什么。如果没有显示的写,可能我们还要去翻看父类去找这个子类到底是哪个接口下的实现类。它增加了继承结构的可跟踪性。

三、成员变量源码详解

    /**
     *默认初始容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 所有空的arrayList共享的实例
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 创建ArrayList对象,未规定大小,默认时使用的数组
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 数组的元素存储在数组缓冲区种。
     * ArrayList的容量是这个数组缓冲区的长度。
     * 当空的 ArrayList 的 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时
     * 则调用扩容函数grow 的基数就是   (DEFAULT_CAPACITY + 需要增加的容量) ,根据这个容量去调用grow
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * ArrayList的大小(它包含的元素数)
     */
    private int size;

        从成员变量可知ArrayList使用数组存储数据(Object[] elementData), 初始容量大小为。在Object[] elementData前面有个transient修饰符,它起到什么作用呢?

         因为我们知道ArrayList继承了序列化接口 java.io.Serializable,那么首先要了解什么是序列化。在我的理解中,序列化就是将一个对象的状态(其实反应一个对象的状态就是对象的所有信息)变成一连串的字节(字符序列)其实到机器中就是 0 ,1的二进制串。当然有序列化就会有反序列化,反序列化也就是把这个字符序列转变成为一个对象。

        那么什么时候会用到序列化?

            1.想把内存中堆中的保存到数据库或者是一个文件当中

            2.想用socket套接字在网络上传输对象

            3.想通过RMI在不同虚拟机间传输对象

        以上三种情况需要用到序列化接口。

        回过头来transient是什么作用呢,使声明为transient的属性不被序列化。想象一下应用场景,就拿冷酷无情的钱相关的示例来说,提款机取钱,我的银行账户是一个对象,我不希望这么绝密的信息在网络中传输,或者被存到哪里,我在取钱的时候用一次用完就不用了。所以为了安全考虑,这些信息就会被加上transient关键字。也就是说,我取钱,我输入了用户名密码如果我的密码被声明为transient,那么这个密码只能在内存中,一旦放到网络中传输或者是存到文件里那么这个密码就会是空的。

四、成员方法源码详解

        看一个类,首先要看成员方法,之后就是看构造方法。构造方法是一个类的入口,具有十分重要的作用。

构造方法:(ArrayList共有三个构造方法,一个无参构造两个带参构造)

    /**
     * 功能:ArrayList无参构造方法
     * 实现:
     * 1.使用默认空数组初始化数据(elementData)
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    /**
     * 功能:ArrayList有参构造方法
     * 参数:集合的初始化容量
     * 实现:
     * 1.如果参数大于0,创建容量为参数大小的Object类型数组并赋给数据缓冲数组
     * 2.如果参数等于0,将空的成员变量数据赋给数据缓冲数组
     * 3.其他情况抛出异常
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    /**
     * 功能:ArrayList有参构造
     * 参数:其他集合
     * 实现:
     * 1.调用集合的toArray方法将参数集合转换成Object数组并赋给数据缓冲数组
     * 2.如果数据缓冲数组elementData大小不等于0:
     *      2.1如果elementData属于Object[]类型,构造方法结束
     *      2.2如果elementData不属于Object[]类型,使用Arrays的copyOf方法创建Object[]类型数组并赋给本身
     * 3.如果数据缓冲数组elementData大小不等于0,将默认的空数组赋给elementData
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

成员方法:

        ArrayList方法相对来说还是较多的,有些方法之间实现方式相似,在这里取一些基础的典型的方法,增删改查,着重解析扩容机制,在加上subList和Iterator。其他的方法不一一赘述。

1.底层数组存储结构扩容机制(核心)

        数组可能是每个人最先了解的数据结构,它有如下特点:

            a.只能存储同一种数据类型。

            b.一旦初始化初始化长度固定。

            c.数组中的元素与元素之间的内存地址是连续的。

            d.Object数组可以存储任意数据类型。(java语言中Object是所有类的根类)

        ArrayList底层存储的数据是数组,所以ArrayList集合具有数组的特点,它同样一次只能存储一种类型数据,在创建集合时使用泛型约束。但是问题又来了,就像上文中所说,如果底层使用的是数组,和集合是怎样可以无限制大小的添加元素的?它是通过扩容的机制来实现,这也是ArrayList集合的核心。下面将通过源码讲解它的实现机制。

    /**
     * 功能:增加指定容量
     * 参数:扩展后集合容量大小(=原始容量大小 + 扩展大小)
     * 实现:
     * 1.确定要扩展的最小容量:
     *      1.1如果集合数据存储数组(elementData)不是默认空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),扩展后集合容量大小(minExpand)为0
     *      1.2如果集合数据存储数组(elementData)是默认空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),扩展后集合容量大小(minExpand)为10(DEFAULT_CAPACITY)
     * 2.将参数(minCapacity)与最小扩展容量(minExpand)比较:
     *      2.1如果大于minExpand,进行ensureExplicitCapacity操作
     *      2.2如果小于minExpand,不进行操作
     * 注:只要集合不是空集合,就会进行ensureExplicitCapacity操作
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

    /**
     * 功能:检查扩展后集合容量大小是否合理(扩展后大小要大于原集合大小)
     * 参数:扩展后集合容量大小(=原始容量大小 + 扩展大小)
     * 实现:
     * 1.超类AbstractList的修改次数计数器modCount+1
     * 2.如果扩展后集合容量大小岛屿原集合大小,就进行扩容操作;否则不进行任何操作
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

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

    /**
     * 功能:真正扩展数据存储集合的方法
     * 参数:扩展后集合容量大小(=原始容量大小 + 扩展大小)
     * 实现:
     * 1.获取原始集合长度(oldCapacity), 计算新集合长度(newCapacity), 默认会将原始集合扩充为原来的1.5倍
     * 2.比较计算后集合长度(newCapacity)与参数要求长度:
     *      2.1如果大于,不进行操作
     *      2.2如果小于,将参数要求大小作为新集合大小
     * 3.新集合大小(newCapacity)与集合最大容量比较(MAX_ARRAY_SIZE默认为Integer最大值 - 8) ---这步也就是要求大小超出集合最大值是做的操作
     *                                                                                       只能分配Integer最大值容量大小的集合
     *      3.1如果小于,进行下一步集合复制操作
     *      3.2如果大于,调用hugeCapacity方法,获取Integer最大值作为集合的最大容量
     * 4.使用Arrays的copyOf方法,指定要复制的数组和要分配的长度,并将复制好的数组重新赋值给集合数据存储数组elementData
     * 注:Arrays的copyOf方法底层调用的是System的arraycopy方法,arraycopy方法是由C/C++实现,有JAVA去调用,至于为什么使用
     *     C/C++去实现,可能是由于C语言操作数据更快。
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

2.set方法 修改固定位置的元素

    /**
     * 功能:修改固定位置的元素
     * 实现:
     * 1.先检查要修改的索引是否越界
     * 2.获取原来该索引处的元素并返回,之后进行修改
     */
    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

3.get方法 获取固定位置的元素

    /**
     * 功能:获取固定位置的元素
     * 实现:
     * 1.检查索引是否越界,获取数组下标对应的元素并返回
     */
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

4.remove相关的方法

    /**
     * 功能:根据索引下标删除元素
     * 实现:
     * 1.通过arraycopy将数组迁移一位
     */
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

5.获取两个结合的交集

    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }

6.batchRemove方法

 private boolean batchRemove(Collection<?> c, boolean complement) {  //complement等于false是取差集,也就是删除和c一样的元素
        final Object[] elementData = this.elementData;                //等于true是取交集   留下和c一样的元素
        int r = 0, w = 0;
        boolean modified = false;//等于false是没修改之前的状态
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)        //挑出不在集合C中的元素,从当前集合的第0个位置开始存放。
                    elementData[w++] = elementData[r];
        } finally {
            <span style="color:#FF0000;">//描述一下这个关键点,首先arrayList在单线程操作的时候,执行到这r一定等于size
            //只有在多线程共同争抢一个资源的时候才会出现 r大于 或者 r小于 的情况。那么我们做一下测试用例</span>
            if (r != size) {            
                 //此时r会比
                 System.arraycopy(elementData, r,                       
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {                //然后从w开始都置换成null并且持久化size的大小
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

7.add相关的方法       无论是哪个add方法,都要先判断是否需要扩容

    public boolean add(E e) {        //添加一个元素不指定位置  默认在容器的最后添加
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    public void add(int index, E element) {      //添加一个元素指定位置
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    public boolean addAll(Collection<? extends E> c) {       //添加一个集合的元素不指定位置   默认在最后
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

    public boolean addAll(int index, Collection<? extends E> c) {       //添加一个集合的元素并指定位置
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

8.readObject和writeObject    这两个函数主要是对输入输出流进行操作 具体看下面代码注释

    private void writeObject(java.io.ObjectOutputStream s)   //接受一个输出流
        throws java.io.IOException{            
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;            
        s.defaultWriteObject();  //对象输出流 s 选择默认写入默认的对象类型
                        //将当前类的非静态和非瞬态字段写入此流。这只能被类的writeObject方法序列化。
        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);        //规定对象输出流s写入对象的个数 
                                //将一个32位int值写入此流
        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {            //写入指定的对象到对象流
            s.writeObject(elementData[i]);        //该对象的类 静态  非静态等字段都写进去
        }
 
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }
 
   
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {//接收一个输入流
        elementData = EMPTY_ELEMENTDATA;
 
        // Read in size, and any hidden stuff
        s.defaultReadObject();        //从当前流中读取当前类的非静态和非瞬态字段。这只能被类的readObject方法反序列化
 
        // Read in capacity
        s.readInt(); // ignored        //读出集合实际元素的数量
 
        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            ensureCapacityInternal(size);
 
            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {        //挨个插入到集合之中
                a[i] = s.readObject();
            }
        }
    }

9.Iterator迭代器

        Iterator迭代器在我的上一篇博客(https://blog.csdn.net/IdealSpring/article/details/81835771)做了详细的讲解,这里只讲解ArrayList之类扩展的新功能forEachRemaining方法。

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {  //对于每个元素执行一个事件直到所有元素迭代完毕
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

使用示例:

    @Test
    public void test1() {
        String[] str = {"大", "王", "叫", "我", "来", "巡", "山"};
        List<String> list = Arrays.asList(str);

        /**
         * 迭代器的forEachRemaining方法和集合本身的forEach方法功能是相同的
         */
        list.forEach(x -> System.out.println(x));
        System.out.println("------------------");
        list.iterator().forEachRemaining(x -> System.out.println(x));
    }

10.ListIterator迭代器 没有新加功能,见上一篇博客https://blog.csdn.net/IdealSpring/article/details/81835771

11.subList方法和SubList类 

     //我们看一下 sublist 方法 
    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(<strong><span style="color:#FF0000;">  this</span></strong>  , 0, fromIndex, toIndex);        //返回一个sublist对象  
    }
    //把当前集合对象的this指针传进了sublist的构造器中
    private class SubList extends AbstractList<E> implements RandomAccess {
        <strong><span style="color:#3333FF;">private final AbstractList<E> parent;</span></strong>        //sublist所有方法都是根据这个引用做的
        private final int parentOffset;
        private final int offset;
        int size;
 
        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {    
            <strong><span style="color:#CC9933;">this.parent = parent;      </span></strong>       //把this对象给了父类引用,由于sublist操作都操作parent,它所有的操作都是对当前集合的操作
            this.parentOffset = fromIndex;            //所以sublist能改变当前集合的值  
            this.offset = offset + fromIndex;            //虽然名义上是集合的一段视图  但是还是能改集合结构
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }

12.spliterator并行遍历迭代器,见上一篇博客https://blog.csdn.net/IdealSpring/article/details/81835771

13.JDK1.8后新添加的方法

a.按条件删除(使用lambda表达式)

    @Override
    public boolean removeIf(Predicate<? super E> filter) {      // //删除所有满足条件的元素   条件是一个拦截器  也就是lamanda表达式
        Objects.requireNonNull(filter);
        // figure out which elements are to be removed
        // any exception thrown from the filter predicate at this stage
        // will leave the collection unmodified
        int removeCount = 0;
        final BitSet removeSet = new BitSet(size);
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            @SuppressWarnings("unchecked")
            final E element = (E) elementData[i];
            if (filter.test(element)) {
                removeSet.set(i);
                removeCount++;
            }
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }

        // shift surviving elements left over the spaces left by removed elements
        final boolean anyToRemove = removeCount > 0;
        if (anyToRemove) {
            final int newSize = size - removeCount;
            for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                i = removeSet.nextClearBit(i);
                elementData[j] = elementData[i];
            }
            for (int k=newSize; k < size; k++) {
                elementData[k] = null;  // Let gc do its work
            }
            this.size = newSize;
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            modCount++;
        }

        return anyToRemove;
    }

使用方法演示:

    @Test
    public void test2() {
        Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        List<Integer> list = new ArrayList<>();
        for (Integer i : arr) {
            list.add(i);
        }
        System.out.println("过滤前:" + list);
        list.removeIf(i -> i % 2 == 0);     // 过去掉偶数
        System.out.println("过滤后:" + list);
    }

运行结果:

b.对集合的所有元素进行操作


    @Override
    @SuppressWarnings("unchecked")
    public void replaceAll(UnaryOperator<E> operator) {    //对原来的集合中的每个元素进行指定的操作
        Objects.requireNonNull(operator);                //用操作的结果替换当前的元素
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            elementData[i] = operator.apply((E) elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

使用方法演示:

    @Test
    public void test3() {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
        System.out.println("操作前:" + list);
        list.replaceAll(x -> x * 10);     // 过去掉偶数
        System.out.println("操作后:" + list);
    }

运行结果:

c.自定义比较器,对集合内元素进行排序

        没有这个方法前要想对集合内元素进行排序,元素必须实现Comparable接口、或者构造时创建Comparator接口的匿名内部函数作为构造器,并且Collection集合家族只有TreeSet支持这样的自定义排序。从JDK1.8以后ArrayList可以使用sort函数进行排序,这是一个很不错的扩展

    @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {     // 对集合中的元素进行排序
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

使用方法演示:

    @Test
    public void test4() {
        List<Integer> list = new ArrayList<>(Arrays.asList(19, 22, 39, 44, 15, 66, 37, 88, 19));
        System.out.println("排序前:" + list);
        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("排序后:" + list);
    }

运行结果:

五、总结

        这样ArrayList怎么实现,方法含义,如何使用就讲解完了。ArrayList基于数组操作,在数值固定的情况下,查改删都还是蛮快捷的,在Java1.8中又加入了新特性,使我们更好更快,更简洁的去操作它的ArrayList。

        对于这个基于数组实现的集合,增删改查操作都是对数组进行的,这个我们看源码是应该很容易理解。在1.8之后,新添加的几个方法都很使用,如果感兴趣,不放使用一下,越用越舒服。

posted @ 2018-08-22 10:55  小情绪Ango  阅读(167)  评论(0编辑  收藏  举报