当前位置: 首页 > news >正文

List 接口

目录

文档链接:

一、List 接口的核心特性

1. 允许重复元素

2. 有序性与索引访问

3. 扩展的迭代器:ListIterator

4. 位置相关的操作

二、ListIterator 

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

1. List.of 的使用

2. List.copyOf 的使用

四、List 接口常用方法

1. 元素访问方法

2. 添加元素方法

3. 删除元素方法

4. 修改元素方法

5. 搜索与比较方法

6. 视图与迭代方法

五、注意事项

1、选择合适的实现类

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 值共存。

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); // 允许多个nullSystem.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 进行双向遍历与修改

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");// 获取ListIteratorListIterator<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 个元素的列表,以及通过可变参数创建任意长度的列表:

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 用于基于已有集合创建不可修改列表,其元素顺序与原集合一致:

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)将列表转为指定类型的数组
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 的所有元素插入到指定索引
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插入15System.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,6System.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()清空列表所有元素
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)根据比较器对列表排序
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为10System.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()返回列表的哈希码(基于元素顺序和值)
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")); // trueSystem.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()返回可分割的迭代器(用于并行处理)
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 // 从指定索引开始的ListIteratorSystem.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

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

// 不推荐: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 方法无法正确计算(递归无限循环),应尽量避免这种用法。

http://www.lryc.cn/news/605366.html

相关文章:

  • 基于动态权重-二维云模型的川藏铁路桥梁施工风险评估MATLAB代码
  • 人形机器人_双足行走动力学:基于OpenSim平台的股骨模型与建模
  • Python并发与性能革命:自由线程、JIT编译器的深度解析与未来展望
  • pytorch入门2:利用pytorch进行概率预测
  • C++中sizeof运算符全面详解和代码示例
  • sqli-labs:Less-5关卡详细解析
  • MySQL学习---分库和分表
  • vulhub ica1靶场攻略
  • GCC链接技术深度解析:性能与空间优化
  • VUE -- 基础知识讲解(二)
  • JavaWeb 核心:AJAX 深入详解与实战(Java 开发者视角)
  • AI 代码助手在大前端项目中的协作开发模式探索
  • Effective C++ 条款12:复制对象时勿忘其每一个成分
  • MATLAB R2023b下载与保姆级安装教程!!
  • 如何读懂 火山方舟 API 部分的内容
  • 《JWT + OAuth2统一认证授权:企业级单点登录方案》
  • SpringBoot之多环境配置全解析
  • Tlias 案例-整体布局(前端)
  • 《大唐孤勇者:韩愈传》读书笔记与经典摘要(二)
  • 【0基础PS】PS工具详解--画笔工具
  • Python 的 match-case
  • 【2025/07/30】GitHub 今日热门项目
  • 数学建模——最大最小化模型
  • “娃哈哈”387件商标还在原集团名下!
  • C++从入门到起飞之——智能指针!
  • Unity UI的未来之路:从UGUI到UI Toolkit的架构演进与特性剖析(5)
  • Tableau 2019可视化数据分析软件安装包下载安装教程
  • 微软:科技领域的创新巨头
  • 华为昇腾NPU卡 文生视频[T2V]大模型WAN2.1模型推理使用
  • 【Qt】QTime::toString(“hh:mm:ss.zzz“) 显示乱码的原因与解决方案