美文网首页Java 杂谈Java
ArrayList 所有方法的使用与源码解析

ArrayList 所有方法的使用与源码解析

作者: 爱打乒乓的程序员 | 来源:发表于2019-07-14 20:46 被阅读1次

    简介

    ArrayList集合类是基于数组实现的,其数组容量的大小可以动态的变化,数组的元素可以为null。值得注意的是,ArrayList中所有的方法都是非线程安全的!在多线程环境下可以使用synchronized或者使用显式锁,当然还有一个更加便捷的方式,就是使用Collections.synchronizedList(List<T> list)方法,这样的话,使用所有的ArrayList方法都是线程安全的。

    List list = Collections.synchronizedList(new ArrayList());
    

    源码剖析

    1.ArrayList集合类的结构图

    ArrayList结构图

    ArrayList集合有3个构造函数:

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    

    ArrayList集合类继承了AbstractList抽象类和实现了List接口,拥有了新增,删除,修改和遍历的功能;实现RandomAccess接口,这是一个标志接口,只要有类实现该接口就代表可以快速随机访问元素;实现Cloneable接口和Serializable接口可以克隆和序列化。

    2.ArrayList集合类的构造函数:

        //参数传入数组长度,可自定义数组大小
        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);
            }
        }
    
        //初始化为默认空数组
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
        //传入集合类型初始化
        public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    

    3.添加元素——add()方法的讲解和ArrayList动态扩容机制

    add(E e)
    add(int index, E element)
    addAll(Collection<? extends E> c)
    addAll(int index, Collection<? extends E> c)
    

    add(E e)方法相关的代码

        protected transient int modCount = 0;
    
        //默认数组容量为10
        private static final int DEFAULT_CAPACITY = 10;
    
        //默认空数组
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
        //ArrayList集合的数组(数组不能被序列化)
        transient Object[] elementData;
    
        //ArrayList的长度
        private int size;
    
        //添加数组元素
        public boolean add(E e) {
            ensureCapacityInternal(size + 1);
            elementData[size++] = e;
            return true;
        }
    
        //如果数组未被初始化,则设置数组默认长度为10
        private void ensureCapacityInternal(int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            ensureExplicitCapacity(minCapacity);
        }
    
        //保证数组长度,如果需要添加元素位置超出数组长度则执行grow扩容
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
        //数组扩容,将原数组复制到新的数组达到扩大数组容量的目的
        private void grow(int minCapacity) {
            int oldCapacity = elementData.length;
            //以原数组长度的1.5倍进行扩容
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    add(int index, E element)相关方法:

        public void add(int index, E element) {
            rangeCheckForAdd(index);
            ensureCapacityInternal(size + 1);//上面有讲解,故省略
            System.arraycopy(elementData, index, elementData, index + 1,
                    size - index);
            elementData[index] = element;
            size++;
        }
    
        //如果添加元素的索引小于0或者超出原数组的长度则抛出异常
        private void rangeCheckForAdd(int index) {
            if (index < 0 || index > this.size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    

    4.删除元素——remove()

    删除的方法有以下3个:

    remove(int index)
    remove(Object o)
    removeAll(Collection<?> c)
    
        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;
        }
    
        public boolean removeAll(Collection<?> c) {
            Objects.requireNonNull(c);//判断对象是否是null
            return batchRemove(c, 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; 
        }
    
        private void rangeCheck(int index) {
            if (index >= size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    
        private boolean batchRemove(Collection<?> c, boolean complement) {
            final Object[] elementData = this.elementData;
            int r = 0, w = 0;
            boolean modified = false;
            try {
                for (; r < size; r++)
                    if (c.contains(elementData[r]) == complement)
                        elementData[w++] = elementData[r];
            } finally {
                // Preserve behavioral compatibility with AbstractCollection,
                // even if c.contains() throws.
                if (r != size) {
                    System.arraycopy(elementData, r,
                                     elementData, w,
                                     size - r);
                    w += size - r;
                }
                if (w != 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;
        }
    

    通过源码可以知道,删除元素都要进行数组的重组,而且当被删除的元素越靠近集合数组的前面,数组的重组开销就越大。

    5.获取元素——get()

    由于ArrayList集合类实现了RandomAccess接口,所以可以快速的获取元素。

        E elementData(int index) {
            return (E) elementData[index];
        }
    
    
        public E get(int index) {
            rangeCheck(index);
    
            return elementData(index);
        }
    

    6.为ArrayList集合类“瘦身”——trimToSize():将数组的容量设置size,省去多余空间,优化程序。

    [图片上传失败...(image-9cca64-1563108252010)]_sample.png)

        public void trimToSize() {
            modCount++;
            if (size < elementData.length) {
                elementData = (size == 0)
                  ? EMPTY_ELEMENTDATA // 当数组长度为0,设置为EMPTY_ELEMENTDATA
                  : Arrays.copyOf(elementData, size); // 重组数组容量,数组容量大小为size
            }
        }
    

    7.指定ArrayList集合类数组容量大小——ensureCapacity(int minCapacity)

        public void ensureCapacity(int minCapacity) {
            // 如果集合数组不是默认的空数组则设置为0,否则设置为默认数组长度
            int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
            if (minCapacity > minExpand) {
                ensureExplicitCapacity(minCapacity); // 保证数组长度,如果minCapacity超出数组长度则执行grow()方法扩容
            }
        }
    

    如果在创建ArrayList对象的时候就可以知道数组长度大概范围,通过ArrayList对象调用ensureCapacity()方法可以达到性能优化的效果。因为如果一开始没有设置容量大小,每当数据添加到数组中,数组容量不足就会动态的扩容,直接将数组元素复制到新的数组,新的数组容量大小为原来的1.5倍,这是非常耗性能的,假设原本的ArrayList对象已经有几十万条的数据,复制到新的数组等于将这几十万条数据的内存复制一遍,容易发生oom。因此如果在使用数组的时候就知道数据量的大小,我们应该先设置容量。

    举个例子:

    public class Sample {
        public static void main(String[] args) {
            System.out.println("不设置集合容量大小,使用默认的容量大小");
            ArrayList arrayList  = new ArrayList();
            arrayList.add("1");
            System.out.println("实际集合容量大小为:" + getArrayListCapacity(arrayList));
            System.out.println("设置容量大小为5");
            arrayList.ensureCapacity(5);
            System.out.println("实际集合容量大小为:" + getArrayListCapacity(arrayList));
            System.out.println("设置容量大小为20");
            arrayList.ensureCapacity(20);
            System.out.println("实际集合容量大小为:" + getArrayListCapacity(arrayList));
        }
    
        // 通过反射获取私有变量--elementData数组的长度
        public static int getArrayListCapacity(ArrayList<?> arrayList) {
            Class<ArrayList> arrayListClass = ArrayList.class;
            try {
                Field field = arrayListClass.getDeclaredField("elementData");
                field.setAccessible(true);
                Object[] objects = (Object[])field.get(arrayList);
                return objects.length;
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
                return -1;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return -1;
            }
        }
    }
    

    输出结果:

    不设置集合容量大小,使用默认的容量大小
    实际集合容量大小为:10
    设置容量大小为5
    实际集合容量大小为:10
    设置容量大小为20
    实际集合容量大小为:20
    

    8.获取数组元素对应的索引——indexOf(Object o)、indexOf(Object o)

        // 返回第一次出现元素的索引,如果没有则返回-1
        public int indexOf(Object o) {
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
    
        // 返回最后一次出现元素的索引,如果没有则返回-1
        public int lastIndexOf(Object o) {
            if (o == null) { 
                for (int i = size-1; i >= 0; i--)
                    if (elementData[i]==null)
                        return i;
            } else { 
                for (int i = size-1; i >= 0; i--)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }
    

    根据源码可以看出,获取元素索引的方法很简单,就是遍历数组,直到遇到第一个或最后一个符合条件的元素,返回索引,如果都没有则返回-1。

    示例:

    public class Sample {
        public static void main(String[] args) {
            ArrayList list = new ArrayList();
            list.add("A");
            list.add("B");
            list.add("C");
            list.add("D");
            list.add("E");
            list.add("A");
            list.add("B");
            System.out.println(list.indexOf("B"));
            System.out.println(list.lastIndexOf("B"));
        }
    }
    

    输出结果:

    1
    6
    

    9.查看是否包含元素——contains(Object o)

        // 是否包含元素
        public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }
    

    源码的实现非常简单,就是通过调用indexOf()的返回值判断是否包含元素,如果返回-1一定就是不包含元素,contains()方法就会返回false,否则返回true。

    10.获取集合长度——size()

        public int size() {
            return size;// size属性的大小是随着集合添加或者删除元素变化
        }
    

    11.ArrayList集合克隆——clone()

       // 重写Object父类的方法
       public Object clone() {
            try {
                ArrayList<?> v = (ArrayList<?>) super.clone();
                v.elementData = Arrays.copyOf(elementData, size);
                v.modCount = 0;
                return v;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError(e);
            }
        }
    

    但有一点需要强调,ArrayList的clone()方法是浅克隆,并非深克隆。

    什么是浅克隆?什么是深克隆?在原型模式中也有相关的概念【鄙人的拙作

    浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
    深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

    示例说明:

    public class Sample {
        public static void main(String[] args) {
            Student student = new Student();
            student.setName("Muggle");
    
            ArrayList<Student> list = new ArrayList();
            list.add(student);
    
            // 浅克隆
            ArrayList<Student> cloneList = (ArrayList) list.clone();
    
            System.out.println("比较被克隆的对象和克隆对象是否一样:" + (list == cloneList));
    
            // 修改集合数组对象的元素
            student.setName("MuggleLee");
    
            System.out.println("被克隆集合的数组第一个元素:" + list.get(0).getName());
            System.out.println("克隆集合的数组第一个元素:" + cloneList.get(0).getName());
        }
    }
    class Student{
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    输出结果:

    比较被克隆的对象和克隆对象是否一样:false
    被克隆集合的数组第一个元素:MuggleLee
    克隆集合的数组第一个元素:MuggleLee
    

    由输出结果可知,被克隆的集合与克隆的集合是不一样的,但是修改元素却都会影响两个集合。

    关于被克隆集合、克隆集合和它们元素的关系如下:

    Clone-sample

    12.集合元素转变成数组——toArray()、toArray(T[] a)

    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
    
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
    

    源码很简单,两个方法其实都是调用System.arraycopy()方法将集合内部的数组内存复制到一个新的数组返回。不过要注意的是toArray()方法,因为容易抛出java.lang.ClassCastException的异常!

    先示范代码:

    public class Sample {
        public static void main(String[] args) {
        ArrayList list = new ArrayList();
            list.add(1);
            list.add(2);
            list.add(3);
            Integer[] integers = (Integer[]) list.toArray();
        }
    }
    

    输出结果报的异常是:

    Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;

    这是因为toArray()返回数组的类型是Object[]类型,如果想通过强转类型为Integer[]或者其他数组属于向下转型,这在Java中是不允许的。

    那有什么办法可以转化为其他类型的数组呢?可以使用另外一种方法 toArray(T[] a) 。通过源码可以看出,toArray(T[] a)方法可以转变为任意类型的数组。

    public class Sample {
        public static void main(String[] args) {
        ArrayList list = new ArrayList();
            list.add(1);
            list.add(2);
            list.add(3);
    
        // 推荐使用的方式。因为新建的数组为0,复制出来的数组长度与集合数组长度一样,不会造成内存浪费
            Integer[] integers = (Integer[]) list.toArray(new Integer[0]);
            System.out.println(integers.length);// 输出结果为3
        }
    }
    

    13.清空集合数组元素——clear()

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

    源码很简单,将集合数组元素全部设置为null,便于垃圾回收。

    14.与Collection对象找交集——retainAll(Collection<?> c)

        public boolean retainAll(Collection<?> c) {
            Objects.requireNonNull(c);
            return batchRemove(c, true);
        }
    
        private boolean batchRemove(Collection<?> c, boolean complement) {
            final Object[] elementData = this.elementData;
            int r = 0, w = 0;
            boolean modified = false;
            try {
                for (; r < size; r++)
                    if (c.contains(elementData[r]) == complement)
                        elementData[w++] = elementData[r];
            } finally {
                // Preserve behavioral compatibility with AbstractCollection,
                // even if c.contains() throws.
                if (r != size) {
                    System.arraycopy(elementData, r,
                                     elementData, w,
                                     size - r);
                    w += size - r;
                }
                if (w != 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;
        }
    

    但有一点需要注意,我们不能够通过retainAll(Collection<?> c)的返回值判断两个集合是否有交集。为什么呢?

    示例:

    public class Sample {
        public static void main(String[] args) {
            // 示例一
            ArrayList list1 = new ArrayList();
            list1.add(1);
            list1.add(2);
    
            LinkedList linkedList1 = new LinkedList();
            linkedList1.add(1);
            linkedList1.add(5);
    
            // 找出与linkedList交集
            boolean flag = list1.retainAll(linkedList1);
            System.out.println("示例一:执行retainAll()后的boolean值:" + flag);
    
            System.out.print("两个集合的交集为:");
            for (int i = 0; i < list1.size(); i++) {
                System.out.print("  " + list1.get(i));
            }
            System.out.println();
    
            // 示例二
            ArrayList list2 = new ArrayList();
            list2.add(1);
            list2.add(2);
    
            LinkedList linkedList2 = new LinkedList();
            linkedList2.add(1);
            linkedList2.add(2);
            linkedList2.add(3);
    
            boolean flag2 = list2.retainAll(linkedList2);
            System.out.println("示例二:执行retainAll()后的boolean值:" + flag2);
    
            System.out.print("两个集合的交集为:");
            for (int i = 0; i < list2.size(); i++) {
                System.out.print("    " + list2.get(i));
            }
        }
    }
    

    输出结果:

    示例一:执行retainAll()后的boolean值:true
    两个集合的交集为:  1
    示例二:执行retainAll()后的boolean值:false
    两个集合的交集为:    1    2
    

    两个集合的关系图如下:

    Collection-relation

    示例一和示例二中两个集合都有交集,但为什么示例二中的retainAll(Collection<?> c)返回值为false?其实retainAll(Collection<?> c)方法的作用是判断调用者是否有元素被移除。如果调用者的元素没有被移除证明 c 集合的元素包含调用者的全部元素。

    15.ArrayList集合排序——sort(Comparator<? super E> c)

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

    根据源码可以知道,使用sort()方法的时候需要传入一个Comparator的对象,那该怎么使用呢?

    示例:

    public class Sample {
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList();
            list.add(1);
            list.add(3);
            list.add(5);
            list.add(4);
            list.add(2);
    
            // 第一种方法:实现Comparator接口的compare方法
            list.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    if(o1 > o2){
                        return 1;
                    }else {
                        return -1;
                    }
                }
            });
    
            // 第二种方式:使用lambda表达式
            //list.sort((x, y) -> x > y ? 1 : -1);
    
            // 第三种方式:使用JDK1.8中Comparator接口提供的排序方法。正叙:naturalOrder() 倒叙:reverseOrder()
            //list.sort(Comparator.naturalOrder());
    
            // 第四种方式:甚至可以不用传Comparator对象,直接传null也可以排序
            //list.sort(null);
    
            for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i) + "    ");
            }
        }
    }
    

    输出结果:

    1    2    3    4    5    
    

    使用ArrayList.sort()方法底层都是调用Arrays.sort()方法【】

    16.多线程遍历集合——spliterator()

    这个高大上的方法可以在集合数据量大的时候结合多线程遍历元素。先通过简单的例子了解是如何使用。

    public class SpliteratorDemo {
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");
            list.add("5");
            list.add("6");
            list.add("7");
            list.add("8");
            Spliterator spliterator1 = list.spliterator();
            Spliterator spliterator2 = spliterator1.trySplit();
            Spliterator spliterator3 = spliterator1.trySplit();
            Spliterator spliterator4 = spliterator2.trySplit();
            System.out.println("spliterator1的长度为:" + spliterator1.estimateSize());
            System.out.println("spliterator2的长度为:" + spliterator2.estimateSize());
            System.out.println("spliterator3的长度为:" + spliterator3.estimateSize());
            System.out.println("spliterator4的长度为:" + spliterator4.estimateSize());
            spliterator1.forEachRemaining(x -> System.out.println("spliterator1对象:"+x));
            spliterator2.forEachRemaining(x -> System.out.println("spliterator2对象:"+x));
            spliterator3.forEachRemaining(x -> System.out.println("spliterator3对象:"+x));
            spliterator4.forEachRemaining(x -> System.out.println("spliterator4对象:"+x));
    
        }
    }
    
    

    输出结果:

    spliterator1的长度为:2
    spliterator2的长度为:2
    spliterator3的长度为:2
    spliterator4的长度为:2
    spliterator1对象:7
    spliterator1对象:8
    spliterator2对象:3
    spliterator2对象:4
    spliterator3对象:5
    spliterator3对象:6
    spliterator4对象:1
    spliterator4对象:2
    

    由输出结果可以知道,通过list.spliterator()创建Spliteror对象,再调用trySplit()方法可以将集合数组平均分配。例子中将集合元素平均分配给4个Spliteror对象,调用forEachRemaining()方法可以遍历循环集合元素。

    在实际开发中,如果集合元素比较多,可以考虑使用Spliteror对象结合多线程并发遍历元素,可以提高效率!不过值得注意的是,使用多线程不一定会提高执行效率的,如果多线程执行的运算不高,CPU算法调度线程有可能将所有线程都分配在一个内核执行,实际上就是单核执行多线程,加上线程之间的上下文切换等时间,多线程的使用反而是降低系统运行效率的!【可以参考我的另外一篇文章

    17.使用forEach(Consumer<? super E> action)迭代集合

        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);// 检查Comsumer对象是否为null
            final int expectedModCount = modCount;
            @SuppressWarnings("unchecked")
            final E[] elementData = (E[]) this.elementData;// 获取集合的数组
            final int size = this.size;// 数组长度
            for (int i=0; modCount == expectedModCount && i < size; i++) {
                action.accept(elementData[i]);// 数组元素作为accept的参数执行accept方法
            }
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
        }
    
    *forEach(Consumer<? super E> action)的行为取决于accept()的实现

    举个例子:

    public class Sample {
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList();
            list.add(1);
            list.add(3);
            list.add(5);
            list.add(4);
            list.add(2);
    
            // 第一种方式:实现Consumer接口的accept方法
    //        list.forEach(new Consumer<Integer>() {
    //            @Override
    //            public void accept(Integer integer) {
    //                System.out.println(integer);
    //            }
    //        });
    
            // 第二种方式:使用lambda表达式
            list.forEach(x -> System.out.print(x + " "));
    
            System.out.println();
    
            // 执行forEach()方法,将数组元素的值都扩大十倍
            list.forEach(x -> System.out.print(x * 10 + " "));
        }
    }
    

    输出结果:

    1 3 5 4 2 
    10 30 50 40 20 
    

    18.移除符合条件的集合元素——removeIf()

        public boolean removeIf(Predicate<? super E> filter) {
            Objects.requireNonNull(filter);// 判断Predicate对象是否为null
            int removeCount = 0;
            final BitSet removeSet = new BitSet(size);// 可以简单理解为一个set集合,作用是记录使test()方法返回true的元素索引
            final int expectedModCount = modCount;
            final int size = this.size;
            // 将test()方法返回true的元素索引set进removeSet里面
            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();
            }
    
            // 如果removeCount大于0代表有集合元素使test()方法返回true,并将集合数组中使test()方法返回true的元素索引移除
            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;
        }
    

    举个例子:

    public class Sample {
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList();
            list.add(1);
            list.add(3);
            list.add(5);
    
            // 第一种方式:实现Predicate接口的test方法
    //        list.removeIf(new Predicate<Integer>() {
    //            @Override
    //            public boolean test(Integer integer) {
    //                return integer == 1;
    //            }
    //        });
    
            // 使用lambda表达式
            list.removeIf(x -> x == 1);
    
            list.forEach(x -> System.out.print(x + " "));
        }
    }
    

    输出结果:

    3 5
    

    19.返回集合区间内的所有元素——subList(int fromIndex, int toIndex)

        public List<E> subList(int fromIndex, int toIndex) {
            subListRangeCheck(fromIndex, toIndex, size);// 检查索引值的范围,如果索引值的范围有错直接抛异常
            return new SubList(this, 0, fromIndex, toIndex);// 返回一个SubList的对象,这个对象与List的对象具有相同的实现,故不再赘述。
        }
    

    举个例子:

    public class Sample {
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList();
            list.add(1);
            list.add(3);
            list.add(5);
            list.add(4);
    
            // 将list集合数组索引为1,2区间的元素赋值给新的集合,值得注意的是,执行subList()方法不会对原集合有影响
            List subList = list.subList(1,2);
            System.out.println("原集合:");
            list.forEach(x-> System.out.print(x + " "));
            System.out.println();
            System.out.println("新的集合:");
            subList.forEach(x-> System.out.print(x + " "));
        }
    }
    

    输出结果:

    原集合:
    1 3 5 4 
    新的集合:
    3 
    

    参考资料:

    https://www.jianshu.com/p/dac220d3d314

    相关文章

      网友评论

        本文标题:ArrayList 所有方法的使用与源码解析

        本文链接:https://www.haomeiwen.com/subject/kuiikctx.html