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

简介

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,省去多余空间,优化程序。

    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

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

推荐阅读更多精彩内容