【Java入门提高篇】Day20 Java容器类详解(三)List接口

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介:   今天要说的是Collection族长下的三名大将之一,List,Set,Queue中的List,它们都继承自Collection接口,所以Collection接口的所有操作,它们自然也是有的。   List,Set,Queue,分别是列表,集合,队列的意思,代表着Collection家族下的三种不同的势力,它们各有所长,也各有所短,就像骑兵,步兵和水兵,各有各的优势,并没有谁一定比谁更好的说法,合适的才是最好的。

  今天要说的是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的常见具体实现类的讲解将在之后的文章里进行说明和比较。

  本篇到此结束,欢迎大家继续关注。

 

真正重要的东西,用眼睛是看不见的。
相关文章
|
1天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
15 6
|
1天前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
【10月更文挑战第16天】Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。通过 hashCode() 和 equals() 方法实现唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 添加和遍历元素,体现了 Set 的高效性和简洁性。
11 4
|
3天前
|
IDE Java 编译器
Java:如何确定编译和运行时类路径是否一致
类路径(Classpath)是JVM用于查找类文件的路径列表,对编译和运行Java程序至关重要。编译时通过`javac -classpath`指定,运行时通过`java -classpath`指定。IDE如Eclipse和IntelliJ IDEA也提供界面管理类路径。确保编译和运行时类路径一致,特别是外部库和项目内部类的路径设置。
|
3天前
|
存储 Java 数据处理
Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。
Java Set:无序之美,不重复之魅!Set 是 Java 集合框架中的一个接口,不包含重复元素且不保证元素顺序。它通过 hashCode() 和 equals() 方法确保元素唯一性,适用于需要唯一性约束的数据处理。示例代码展示了如何使用 HashSet 实现这一特性。
12 5
|
1天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
7 2
|
2天前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
10 2
|
3天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。
【10月更文挑战第14天】从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其独特的“不重复性”要求,彻底改变了处理唯一性约束数据的方式。本文深入探讨Set的核心理念,并通过示例代码展示了HashSet和TreeSet的特点和应用场景。
9 2
|
4月前
|
安全 Java
java线程之List集合并发安全问题及解决方案
java线程之List集合并发安全问题及解决方案
781 1
|
3月前
|
Java API Apache
怎么在在 Java 中对List进行分区
本文介绍了如何将列表拆分为给定大小的子列表。尽管标准Java集合API未直接支持此功能,但Guava和Apache Commons Collections提供了相关API。
|
3月前
|
运维 关系型数据库 Java
PolarDB产品使用问题之使用List或Range分区表时,Java代码是否需要进行改动
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。