【Java入门提高篇】Day20 Java容器类详解(三)List接口
今天要说的是Collection族长下的三名大将之一,List,Set,Queue中的List,它们都继承自Collection接口,所以Collection接口的所有操作,它们自然也是有的。
List,Set,Queue,分别是列表,集合,队列的意思,代表着Collection家族下的三种不同的势力,它们各有所长,也各有所短,就像骑兵,步兵和水兵,各有各的优势,并没有谁一定比谁更好的说法,合适的才是最好的。接下来,将会分别介绍这三名大将,从中你也会看到它们各自的特点。
本篇先来介绍一下List接口。
我们先来看看List的源码:
public interface List<E> extends Collection<E> { // 查询接口 /** * 列表元素个数 */ int size(); /** * 是否为空 */ boolean isEmpty(); /** * 是否包含某元素 */ boolean contains(Object o); /** * 返回一个List迭代器 */ Iterator<E> iterator(); /** * 将List转换为Object数组 */ Object[] toArray(); /** * 转换为指定类型数组 */ <T> T[] toArray(T[] a); // 修改操作 /** * 添加元素,成功返回true */ boolean add(E e); /** * 移除某一个元素,成功返回true */ boolean remove(Object o); // 批量操作 /** * 判断是否包含集合C 中的所有元素 */ boolean containsAll(Collection<?> c); /** * 将集合C 中所有元素添加到列表 */ boolean addAll(Collection<? extends E> c); /** * 将集合C 中所有元素添加到列表,添加在序号为index的元素之后 */ boolean addAll(int index, Collection<? extends E> c); /** * 从列表中移除集合C 中所有元素 */ boolean removeAll(Collection<?> c); /** * 从列表中移除所有不在集合C 中的元素 */ boolean retainAll(Collection<?> c); /** * 全部替换 */ default void replaceAll(UnaryOperator<E> operator) { Objects.requireNonNull(operator); final ListIterator<E> li = this.listIterator(); while (li.hasNext()) { li.set(operator.apply(li.next())); } } /** * 根据指定的比较器来排序,如果传入的比较器是null,则元素必须实现Comparable 接口 */ @SuppressWarnings({"unchecked", "rawtypes"}) default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } } /** * 移除所有元素 */ void clear(); // 比较和hash boolean equals(Object o); int hashCode(); // 根据序号进行的操作 /** * 获取指定序号的元素 */ E get(int index); /** * 替换指定序号的元素 */ E set(int index, E element); /** * 在指定序号的元素后插入元素 */ void add(int index, E element); /** * 移除指定序号的元素 */ E remove(int index); // 搜索操作 /** * 返回元素第一次出现的位置,如果未找到则返回-1 */ int indexOf(Object o); /** * 返回元素出现的最后一个位置 */ int lastIndexOf(Object o); // List迭代器 /** * 返回一个List迭代器 */ ListIterator<E> listIterator(); /** * 返回一个序号从Index开始的List迭代器 */ ListIterator<E> listIterator(int index); // 视图 /** * 返回一个子队列,序列从fromIndex到toIndex,包含fromIndex,不包含toIndex * 对子队列的修改会影响原队列 * 如果原队列修改,那么对子队列的影响是未定义的 */ java.util.List<E> subList(int fromIndex, int toIndex); /** * 创建一个可分割的迭代器(用于并行计算) */ @Override default Spliterator<E> spliterator() { return Spliterators.spliterator(this, Spliterator.ORDERED); } }
其实JDK里的注释已经十分丰富,大家平时有时间可以多看看,为了方便阅读,我这里用简单粗暴的语言进行了精简翻译。
List即列表,存储的是有序集合,里面的元素有序存储,可以重复,所谓有序集合,顾名思义,就是里面的元素存放是有顺序的,每个插入的元素都对应着一个序号,可以根据序号获取元素。
List支持的操作也很丰富,最常用的增删改查,批量添加,批量替换,批量删除,还有搜索,排序操作,还支持普通迭代器和可分割式迭代器,前者主要用于遍历,后者则主要用于并行式计算,关于迭代器的知识后面会统一介绍。下面是使用常见操作的一个小栗子:
public class Test { public static void main(String[] args){ test(); } static void test(){ List<Integer> integers = new ArrayList<>(); List<Integer> integersA = new ArrayList<>(); //添加元素 integers.add(1); integers.add(2); integers.add(3); integers.add(4); integersA.add(1); integersA.add(2); integersA.add(33); integersA.add(44); System.out.println("列表大小:" + integers.size()); System.out.println("是否为空:" + integers.isEmpty()); System.out.println("是否包含某元素:" + integers.contains(2)); System.out.println("是否包含全部元素:" + integers.containsAll(integersA)); //转换为数组 Integer[] integerArray = integers.toArray(new Integer[0]); System.out.println("遍历数组:"); for (int i = 0; i < integerArray.length; i++){ System.out.println(integerArray[i]); } System.out.println("当前列表integers:" + integers); //批量添加 System.out.println("批量添加元素"); integers.addAll(integersA); System.out.println("当前列表integers:" + integers); //移除元素 System.out.println("移除元素"); integers.remove(1); System.out.println("当前列表integers:" + integers); //批量移除 System.out.println("批量移除元素"); integers.removeAll(integersA); System.out.println("当前列表integers:" + integers); //开始替换 System.out.println("批量替换元素"); integers.replaceAll(it -> it + 1); System.out.println("当前列表integers:" + integers); //从列表中移除所有不在集合integersA中的元素 integersA.add(2); integersA.add(4); System.out.println("保留元素"); integers.retainAll(integersA); System.out.println("当前列表integers:" + integers); //插入 System.out.println("开始插入"); System.out.println("当前列表integersA:" + integersA); integersA.add(2,155); integersA.add(1,125); System.out.println("当前列表integersA:" + integersA); //排序 System.out.println("开始排序——使用内部比较器"); integersA.sort(null); System.out.println("当前列表integersA:" + integersA); System.out.println("开始排序——使用外部比较器"); integersA.sort((itA, itB) -> itB - itA); System.out.println("当前列表integersA:" + integersA); //序号操作 Integer a = integersA.get(2); System.out.println("integersA第三个元素是:" + a); System.out.println("开始替换"); integersA.set(3, 66); System.out.println("当前列表integersA:" + integersA); System.out.println("开始移除"); integersA.remove(3); System.out.println("当前列表integersA:" + integersA); //搜索操作 System.out.println("查找元素2(第一次出现)位置:" + integersA.indexOf(2)); System.out.println("查找元素2(最后一次出现)位置:" + integersA.lastIndexOf(2)); //子队列操作 List<Integer> subList = integersA.subList(0, 4); System.out.println("子队列:" + subList); subList.add(5); subList.add(5); subList.add(5); System.out.println("当前子列表:" + subList); System.out.println("当前列表integersA:" + integersA); integersA.add(1, 233); integersA.add(1, 233); integersA.add(1, 233); System.out.println("当前列表integersA:" + integersA); System.out.println("当前子列表:" + subList); } }
大家可以先想想结果,再下看面的答案。
实际输出如下:
列表大小:4 是否为空:false 是否包含某元素:true 是否包含全部元素:false 遍历数组: 1 2 3 4 当前列表integers:[1, 2, 3, 4] 批量添加元素 当前列表integers:[1, 2, 3, 4, 1, 2, 33, 44] 移除元素 当前列表integers:[1, 3, 4, 1, 2, 33, 44] 批量移除元素 当前列表integers:[3, 4] 批量替换元素 当前列表integers:[4, 5] 保留元素 当前列表integers:[4] 开始插入 当前列表integersA:[1, 2, 33, 44, 2, 4] 当前列表integersA:[1, 125, 2, 155, 33, 44, 2, 4] 开始排序——使用内部比较器 当前列表integersA:[1, 2, 2, 4, 33, 44, 125, 155] 开始排序——使用外部比较器 当前列表integersA:[155, 125, 44, 33, 4, 2, 2, 1] integersA第三个元素是:44 开始替换 当前列表integersA:[155, 125, 44, 66, 4, 2, 2, 1] 开始移除 当前列表integersA:[155, 125, 44, 4, 2, 2, 1] 查找元素33(第一次出现)位置:4 查找元素33(最后一次出现)位置:5 子队列:[155, 125, 44, 4] 当前子列表:[155, 125, 44, 4, 5, 5, 5] 当前列表integersA:[155, 125, 44, 4, 5, 5, 5, 2, 2, 1] 当前列表integersA:[155, 233, 233, 233, 125, 44, 4, 5, 5, 5, 2, 2, 1] Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239) at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099) at java.util.AbstractList.listIterator(AbstractList.java:299) at java.util.ArrayList$SubList.iterator(ArrayList.java:1095) at java.util.AbstractCollection.toString(AbstractCollection.java:454) at java.lang.String.valueOf(String.java:2994) at java.lang.StringBuilder.append(StringBuilder.java:131) at com.frank.chapter20.Test.test(Test.java:115) at com.frank.chapter20.Test.main(Test.java:15)
不知道符不符合你的预期,这里关于内部比较器和外部比较器的知识只一笔带过,Integer类型是实现了Comparable接口的,所以sort方法传入null时会使用Integer的内部比较器进行排序,而使用外部比较器时,使用的是Java8的新特性,lamada表达式,省去了方法名和参数类型,因为函数式接口不存在重载方法,所以编译器可以推断出参数类型,这样就不用再大费周章的用new语法去创建一个比较器(当然,只是语法糖而已,如果不是很理解比较器,可以先行百度,后面的文章里也会有详细介绍)。在最后报出了一个ConcurrentModificationException,因为原队列修改后,子队列视图就被破坏了,所以再次访问子视图时就会报错。
List是最常用的容器类,List最大的特点便是要求元素有序存储,List跟数组相比,最大的优势在于List大小可以动态扩展,但数组支持随机存取,所以当元素个数的固定的时候,使用数组往往效率更高。(当然,一般情况下还是使用List吧,因为支持的操作更加丰富,比如进行排序时不需要自己写算法)。
一般来说,对元素没有特殊要求,不需要去重存储,没有先进先出的要求的场景下,List是最好的选择。
List接口下有多个常用的实现类,每个类都有其特点,具体选择哪种类需要根据实际情况进行选择。
希望大家能通过这篇文章,了解List的主要方法及其使用方法以及常用场景,关于List的常见具体实现类的讲解将在之后的文章里进行说明和比较。
本篇到此结束,欢迎大家继续关注。