List 接口

目录

文档链接:

[一、List 接口的核心特性](#一、List 接口的核心特性)

[1. 允许重复元素](#1. 允许重复元素)

[2. 有序性与索引访问](#2. 有序性与索引访问)

[3. 扩展的迭代器:ListIterator](#3. 扩展的迭代器:ListIterator)

[4. 位置相关的操作](#4. 位置相关的操作)

二、ListIterator

[三、不可修改列表:List.of 与 List.copyOf](#三、不可修改列表:List.of 与 List.copyOf)

[1. List.of 的使用](#1. List.of 的使用)

[2. List.copyOf 的使用](#2. List.copyOf 的使用)

[四、List 接口常用方法](#四、List 接口常用方法)

[1. 元素访问方法](#1. 元素访问方法)

[2. 添加元素方法](#2. 添加元素方法)

[3. 删除元素方法](#3. 删除元素方法)

[4. 修改元素方法](#4. 修改元素方法)

[5. 搜索与比较方法](#5. 搜索与比较方法)

[6. 视图与迭代方法](#6. 视图与迭代方法)

五、注意事项

1、选择合适的实现类

[2、避免在循环中使用索引访问 LinkedList](#2、避免在循环中使用索引访问 LinkedList)

3、谨慎使用线性搜索方法

4、不可修改列表的使用场景

5、避免列表包含自身作为元素


在 Java 集合框架中,List 接口是最常用的接口之一,它继承自 Collection 接口,为元素的有序存储和访问提供了丰富的功能。与 Set 等接口不同,List 允许重复元素,支持基于索引的访问。

文档链接:

点击此处,List文档链接

一、List 接口的核心特性

List 接口作为Collection的子接口,在保留 Collection 基本功能的基础上,增加了许多针对 "有序集合" 的特性,主要包括:

1. 允许重复元素

Set接口不同,List允许存在多个相等的元素(即**e1.equals(e2)true的元素)。例如,一个List<String>可以同时包含多个 "hello" 字符串;如果实现类允许null** 元素,List 还支持**++多个++** null 值共存。

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("hello"); // 允许重复元素
        list.add(null);
        list.add(null); // 允许多个null
        System.out.println(list); // 输出:[hello, hello, null, null]
    }
}

2. 有序性与索引访问

List 中的元素具有++明确++的顺序(即插入顺序),且支持基于索引(从零开始)的访问,类似数组。这意味着我们可以通过索引直接获取、修改、插入或删除元素。

3. 扩展的迭代器:ListIterator

List 提供了专门的迭代器++ListIterator++ ,相比普通的++Iterator++ ,它支持双向遍历 (向前 / 向后移动)、元素插入元素替换等功能,更适合 List 的特性。

4. 位置相关的操作

List 新增了大量与位置相关的方法,例如 get(int index)(获取指定索引元素)、add(int index, E element) (指定位置插入)、remove(int index)(删除指定索引元素)等,这些方法是 List 区别于其他 Collection 子接口的核心。

二、ListIterator

**ListIterator **是 List 接口提供的特殊迭代器,它继承自 Iterator ,并扩展了更多功能。其核心方法包括:

  • hasNext():是否有下一个元素(正向遍历)
  • next():获取下一个元素
  • hasPrevious():是否有上一个元素(反向遍历)
  • previous():获取上一个元素
  • add(E e):在当前位置插入元素
  • set(E e):替换当前迭代的元素
  • remove():删除当前迭代的元素
    代码示例:使用 ListIterator 进行双向遍历与修改
java 复制代码
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ListIteratorDemo {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("apple");
        fruits.add("banana");
        fruits.add("cherry");

        // 获取ListIterator
        ListIterator<String> iterator = fruits.listIterator();

        // 正向遍历并修改元素
        System.out.println("正向遍历:");
        while (iterator.hasNext()) {
            String fruit = iterator.next();
            System.out.print(fruit + " ");
            if (fruit.equals("banana")) {
                iterator.set("grape"); // 将banana替换为grape
            }
        }
        // 输出:正向遍历:apple banana cherry 

        // 反向遍历并插入元素
        System.out.println("\n反向遍历:");
        while (iterator.hasPrevious()) {
            String fruit = iterator.previous();
            System.out.print(fruit + " ");
            if (fruit.equals("apple")) {
                iterator.add("orange"); // 在apple前插入orange
            }
        }
        // 输出:反向遍历:grape apple 

        System.out.println("\n最终列表:" + fruits); 
        // 输出:最终列表:[orange, apple, grape, cherry]
    }
}

从示例可见,ListIterator 不仅能双向遍历,还能在遍历过程中修改列表,这比普通Iterator更灵活,

但也需要注意:

遍历过程中若通过 List 的方法(如 add/remove)修改列表,会导致迭代器抛出ConcurrentModificationException,因此建议通过迭代器自身的方法修改元素。

三、不可修改列表:List.of 与 List.copyOf

Java 9 引入了List.ofList.copyOf两个静态工厂方法,用于创建不可修改的列表。这类列表具有以下特性:

  • 不可修改 :调用addremoveset等修改方法会抛出UnsupportedOperationException
  • 不允许 null 元素 :创建时包含 null 会抛出NullPointerException
  • 值传递:若元素本身可变,列表内容可能间接变化(但列表结构不变);
  • 支持序列化:当所有元素可序列化时,列表可序列化。

1. List.of 的使用

**List.of **提供了多个重载方法,支持创建包含++0 到 10 个元素++的列表,以及通过可变参数创建任意长度的列表:

java 复制代码
import java.util.List;

public class ListOfDemo {
    public static void main(String[] args) {
        // 创建空列表
        List<String> emptyList = List.of();

        // 创建包含1个元素的列表
        List<Integer> singleList = List.of(100);

        // 创建包含3个元素的列表
        List<Double> tripleList = List.of(1.1, 2.2, 3.3);

        // 通过可变参数创建列表
        List<String> varArgsList = List.of("a", "b", "c", "d");

        // 尝试修改会抛出异常
        try {
            varArgsList.add("e");
        } catch (UnsupportedOperationException e) {
            System.out.println("不可修改列表:" + e.getMessage());
        }

        // 尝试添加null会抛出异常
        try {
            List.of(null);
        } catch (NullPointerException e) {
            System.out.println("不允许null元素:" + e.getMessage());
        }
    }
}

2. List.copyOf 的使用

List.copyOf 用于基于已有集合创建不可修改列表,其元素顺序与原集合一致:

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class ListCopyOfDemo {
    public static void main(String[] args) {
        List<String> original = new ArrayList<>();
        original.add("java");
        original.add("python");

        // 基于original创建不可修改列表
        List<String> copy = List.copyOf(original);

        // 原列表修改不影响copy(copy是快照)
        original.add("c++");
        System.out.println("原列表:" + original); // [java, python, c++]
        System.out.println("copy列表:" + copy); // [java, python]

        // 尝试修改copy抛出异常
        try {
            copy.remove(0);
        } catch (UnsupportedOperationException e) {
            System.out.println("copy列表不可修改:" + e.getMessage());
        }
    }
}

注意List.copyOf 会对原集合进行 "快照",原集合后续的修改不会影响复制后的列表; 若原集合包含 null 元素,List.copyOf 会抛出**NullPointerException**。

四、List 接口常用方法

List接口提供了丰富的方法,可分为元素访问添加元素删除元素修改元素搜索与比较批量操作 等类别。以下是常用方法的详细说明及代码示例(以ArrayList为例,它实现了 List 的所有可选操作)。

1. 元素访问方法

方法 描述
E get(int index) 返回指定索引的元素
int size() 返回列表元素个数
boolean isEmpty() 判断列表是否为空
Object[] toArray() 将列表转为数组(返回 Object [])
<T> T[] toArray(T[] a) 将列表转为指定类型的数组
java 复制代码
import java.util.ArrayList;
import java.util.List;

public class ElementAccessDemo {
    public static void main(String[] args) {
        List<String> colors = new ArrayList<>();
        colors.add("red");
        colors.add("green");
        colors.add("blue");

        // 获取指定索引元素
        System.out.println("索引1的元素:" + colors.get(1)); // green

        // 获取元素个数
        System.out.println("元素个数:" + colors.size()); // 3

        // 判断是否为空
        System.out.println("是否为空:" + colors.isEmpty()); // false

        // 转为Object数组
        Object[] objArray = colors.toArray();
        System.out.println("Object数组:" + objArray[0]); // red

        // 转为指定类型数组
        String[] strArray = colors.toArray(new String[0]);
        System.out.println("String数组:" + strArray[2]); // blue
    }
}

2. 添加元素方法

方法 描述
boolean add(E e) 在列表末尾添加元素(返回是否成功)
void add(int index, E element) 在指定索引插入元素(后续元素后移)
boolean addAll(Collection<? extends E> c) 将集合 c 的所有元素添加到列表末尾
boolean addAll(int index, Collection<? extends E> c) 将集合 c 的所有元素插入到指定索引
java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class AddElementDemo {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();

        // 末尾添加元素
        numbers.add(10);
        numbers.add(20);
        System.out.println("添加后:" + numbers); // [10, 20]

        // 指定位置插入元素
        numbers.add(1, 15); // 在索引1插入15
        System.out.println("插入后:" + numbers); // [10, 15, 20]

        // 添加另一个集合的所有元素(末尾)
        List<Integer> moreNumbers = Arrays.asList(30, 40);
        numbers.addAll(moreNumbers);
        System.out.println("批量添加后:" + numbers); // [10, 15, 20, 30, 40]

        // 插入另一个集合的所有元素(指定位置)
        List<Integer> prefix = Arrays.asList(5, 6);
        numbers.addAll(0, prefix); // 在索引0插入5,6
        System.out.println("指定位置批量插入后:" + numbers); // [5, 6, 10, 15, 20, 30, 40]
    }
}

注意

add(int index, E)addAll(int index, ...)会导致索引index及之后的元素后移,对于LinkedList(链表实现),这种操作效率较高;但对于ArrayList(数组实现),可能需要复制数组,索引越大效率越低。

3. 删除元素方法

方法 描述
E remove(int index) 删除指定索引的元素,返回被删除的元素
boolean remove(Object o) 删除第一个与 o 相等的元素(返回是否删除成功)
boolean removeAll(Collection<?> c) 删除列表中所有在集合 c 中的元素(差集)
boolean retainAll(Collection<?> c) 保留列表中所有在集合 c 中的元素(交集)
void clear() 清空列表所有元素
java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class RemoveElementDemo {
    public static void main(String[] args) {
        List<String> languages = new ArrayList<>(Arrays.asList("java", "python", "c", "java", "go"));

        // 删除指定索引元素
        String removed = languages.remove(2); // 删除索引2的"c"
        System.out.println("删除的元素:" + removed + ",剩余:" + languages); // [java, python, java, go]

        // 删除第一个匹配的元素
        boolean isRemoved = languages.remove("java"); // 删除第一个"java"
        System.out.println("是否删除成功:" + isRemoved + ",剩余:" + languages); // [python, java, go]

        // 删除与集合c交集的元素(removeAll)
        List<String> toRemove = Arrays.asList("python", "c++");
        languages.removeAll(toRemove);
        System.out.println("removeAll后:" + languages); // [java, go]

        // 保留与集合c交集的元素(retainAll)
        List<String> toRetain = Arrays.asList("java", "python");
        languages.retainAll(toRetain);
        System.out.println("retainAll后:" + languages); // [java]

        // 清空列表
        languages.clear();
        System.out.println("清空后:" + languages); // []
    }
}

4. 修改元素方法

方法 描述
E set(int index, E element) 替换指定索引的元素,返回被替换的元素
default void replaceAll(UnaryOperator<E> operator) 用函数运算结果替换所有元素
default void sort(Comparator<? super E> c) 根据比较器对列表排序
java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.UnaryOperator;

public class ModifyElementDemo {
    public static void main(String[] args) {
        List<Integer> nums = new ArrayList<>(Arrays.asList(3, 1, 4, 1, 5));

        // 替换指定索引元素
        Integer old = nums.set(2, 10); // 替换索引2的4为10
        System.out.println("被替换的元素:" + old + ",替换后:" + nums); // [3, 1, 10, 1, 5]

        // 批量替换(每个元素乘2)
        UnaryOperator<Integer> doubleOp = n -> n * 2;
        nums.replaceAll(doubleOp);
        System.out.println("批量替换后:" + nums); // [6, 2, 20, 2, 10]

        // 排序(默认升序)
        nums.sort(Comparator.naturalOrder());
        System.out.println("升序排序后:" + nums); // [2, 2, 6, 10, 20]

        // 排序(降序)
        nums.sort(Comparator.reverseOrder());
        System.out.println("降序排序后:" + nums); // [20, 10, 6, 2, 2]
    }
}

5. 搜索与比较方法

方法 描述
int indexOf(Object o) 返回元素 o 第一次出现的索引(不存在返回 - 1)
int lastIndexOf(Object o) 返回元素 o 最后一次出现的索引(不存在返回 - 1)
boolean contains(Object o) 判断列表是否包含元素 o
boolean containsAll(Collection<?> c) 判断列表是否包含集合 c 的所有元素
boolean equals(Object o) 判断与另一个对象是否相等(列表需元素顺序和值均相同)
int hashCode() 返回列表的哈希码(基于元素顺序和值)
java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class SearchCompareDemo {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>(Arrays.asList("apple", "banana", "apple", "orange"));

        // 查找第一次出现的索引
        System.out.println("apple第一次出现的索引:" + fruits.indexOf("apple")); // 0

        // 查找最后一次出现的索引
        System.out.println("apple最后一次出现的索引:" + fruits.lastIndexOf("apple")); // 2

        // 判断是否包含元素
        System.out.println("是否包含banana:" + fruits.contains("banana")); // true
        System.out.println("是否包含grape:" + fruits.contains("grape")); // false

        // 判断是否包含集合所有元素
        List<String> subset = Arrays.asList("apple", "banana");
        System.out.println("是否包含subset所有元素:" + fruits.containsAll(subset)); // true

        // 比较两个列表是否相等(顺序和元素均需相同)
        List<String> another = new ArrayList<>(Arrays.asList("apple", "banana", "apple", "orange"));
        System.out.println("两个列表是否相等:" + fruits.equals(another)); // true

        // 哈希码(相同元素和顺序的列表哈希码相同)
        System.out.println("fruits的哈希码:" + fruits.hashCode());
        System.out.println("another的哈希码:" + another.hashCode()); // 与fruits相同
    }
}

注意

indexOf lastIndexOf 通过***equals*** 方法判断元素相等,因此若元素重写了equals,需确保逻辑正确;此外,这两个方法是线性搜索(时间复杂度 O (n)),对于大型列表,频繁调用可能影响性能。

6. 视图与迭代方法

方法 描述
List<E> subList(int fromIndex, int toIndex) 返回从 fromIndex(含)到 toIndex(不含)的子列表视图
Iterator<E> iterator() 返回普通迭代器(正向遍历)
ListIterator<E> listIterator() 返回 ListIterator(双向遍历)
ListIterator<E> listIterator(int index) 从指定索引开始的 ListIterator
default Spliterator<E> spliterator() 返回可分割的迭代器(用于并行处理)
java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

public class ViewIteratorDemo {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));

        // 获取子列表(视图,修改会影响原列表)
        List<Integer> subList = numbers.subList(1, 4); // 索引1到3(元素2,3,4)
        System.out.println("子列表:" + subList); // [2, 3, 4]

        // 修改子列表,原列表也会变化
        subList.set(0, 20);
        System.out.println("修改子列表后,原列表:" + numbers); // [1, 20, 3, 4, 5, 6]

        // 普通迭代器(正向遍历)
        System.out.print("普通迭代器遍历:");
        for (Integer num : numbers) { // 底层使用iterator()
            System.out.print(num + " ");
        }
        // 输出:1 20 3 4 5 6 

        // 从指定索引开始的ListIterator
        System.out.print("\n从索引3开始的ListIterator:");
        ListIterator<Integer> lit = numbers.listIterator(3);
        while (lit.hasNext()) {
            System.out.print(lit.next() + " ");
        }
        // 输出:4 5 6 
    }
}

注意

subList 返回的是原列表的视图而非新列表。对子列表的修改(如添加、删除、替换)会直接影响原列表,且原列表的结构修改(如添加 / 删除元素)会导致子列表抛出ConcurrentModificationException

五、注意事项

1、选择合适的实现类

List 的常用实现类有**ArrayList**(数组实现)和 **LinkedList(**链表实现):

  • ArrayList:随机访问快(get/set效率高),但插入 / 删除中间元素效率低(需移动元素);
  • LinkedList:插入 / 删除中间元素快,但随机访问效率低(需遍历链表)。
  • 若频繁访问元素,优先选ArrayList;若频繁插入 / 删除中间元素,可考虑LinkedList

2、避免在循环中使用索引访问 LinkedList

对于 LinkedList get(index) 方法需要++从表头 / 表尾遍++ 历到指定索引,时间复杂度为 O (n)。若在循环中多次调用 get(index),总时间复杂度会变为 O (n²),严重影响性能。建议使用迭代器或增强 for 循环遍历:

java 复制代码
// 不推荐:LinkedList循环中使用get(index)
List<String> linkedList = new LinkedList<>();
for (int i = 0; i < linkedList.size(); i++) {
    System.out.println(linkedList.get(i)); // 效率低
}

// 推荐:使用增强for循环(底层是迭代器)
for (String s : linkedList) {
    System.out.println(s); // 效率高
}

3、谨慎使用线性搜索方法

indexOflastIndexOfcontains 等方法均为线性搜索(O (n)),对于大型列表(如 10 万级元素),频繁调用会导致性能问题。若需频繁搜索,可考虑先将 List 转为HashSet(搜索效率 O (1)),但需注意元素顺序和重复问题。

4、不可修改列表的使用场景

List.of 和**List.copyOf **创建的不可修改列表适合用于:

  • 存储常量数据(如配置项、枚举值);
  • 作为方法返回值,防止外部修改内部列表;
  • 多线程环境下,避免并发修改问题(无需额外同步)。

5、避免列表包含自身作为元素

虽然 List 允许包含自身作为元素,但会导致**equals ** 和 **hashCode **方法无法正确计算(递归无限循环),应尽量避免这种用法。

相关推荐
Fengshana33 分钟前
王道考研-数据结构-01
数据结构·考研
程序员良辰2 小时前
Spring与SpringBoot:从手动挡到自动挡的Java开发进化论
java·spring boot·spring
鹦鹉0072 小时前
SpringAOP实现
java·服务器·前端·spring
练习时长两年半的程序员小胡3 小时前
JVM 性能调优实战:让系统性能 “飞” 起来的核心策略
java·jvm·性能调优·jvm调优
崎岖Qiu3 小时前
【JVM篇11】:分代回收与GC回收范围的分类详解
java·jvm·后端·面试
27669582925 小时前
东方航空 m端 wasm req res分析
java·python·node·wasm·东方航空·东航·东方航空m端
许苑向上5 小时前
Spring Boot 自动装配底层源码实现详解
java·spring boot·后端
喵叔哟5 小时前
31.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--财务服务--收支分类
java·微服务·.net
codu4u13146 小时前
Maven中的bom和父依赖
java·linux·maven
呦呦鹿鸣Rzh6 小时前
微服务快速入门
java·微服务·架构