龙渊幻想-异世界冒险活动站

龙渊幻想-异世界冒险活动站

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可以同时包含多个 "hello" 字符串;如果实现类允许 null 元素,List 还支持多个 null 值共存。

import java.util.ArrayList;

import java.util.List;

public class ListDemo {

public static void main(String[] args) {

List 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 进行双向遍历与修改

import java.util.ArrayList;

import java.util.List;

import java.util.ListIterator;

public class ListIteratorDemo {

public static void main(String[] args) {

List fruits = new ArrayList<>();

fruits.add("apple");

fruits.add("banana");

fruits.add("cherry");

// 获取ListIterator

ListIterator 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.of和List.copyOf两个静态工厂方法,用于创建不可修改的列表。这类列表具有以下特性:

不可修改:调用add、remove、set等修改方法会抛出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 emptyList = List.of();

// 创建包含1个元素的列表

List singleList = List.of(100);

// 创建包含3个元素的列表

List tripleList = List.of(1.1, 2.2, 3.3);

// 通过可变参数创建列表

List 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 original = new ArrayList<>();

original.add("java");

original.add("python");

// 基于original创建不可修改列表

List 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[] toArray(T[] a)将列表转为指定类型的数组

import java.util.ArrayList;

import java.util.List;

public class ElementAccessDemo {

public static void main(String[] args) {

List 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 c)将集合 c 的所有元素添加到列表末尾boolean addAll(int index, Collection c)将集合 c 的所有元素插入到指定索引

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

public class AddElementDemo {

public static void main(String[] args) {

List 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 moreNumbers = Arrays.asList(30, 40);

numbers.addAll(moreNumbers);

System.out.println("批量添加后:" + numbers); // [10, 15, 20, 30, 40]

// 插入另一个集合的所有元素(指定位置)

List 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()清空列表所有元素

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

public class RemoveElementDemo {

public static void main(String[] args) {

List 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 toRemove = Arrays.asList("python", "c++");

languages.removeAll(toRemove);

System.out.println("removeAll后:" + languages); // [java, go]

// 保留与集合c交集的元素(retainAll)

List 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 operator)用函数运算结果替换所有元素default void sort(Comparator 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 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 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)判断列表是否包含元素 oboolean 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 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 subset = Arrays.asList("apple", "banana");

System.out.println("是否包含subset所有元素:" + fruits.containsAll(subset)); // true

// 比较两个列表是否相等(顺序和元素均需相同)

List 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 subList(int fromIndex, int toIndex)返回从 fromIndex(含)到 toIndex(不含)的子列表视图Iterator iterator()返回普通迭代器(正向遍历)ListIterator listIterator()返回 ListIterator(双向遍历)ListIterator listIterator(int index)从指定索引开始的 ListIteratordefault Spliterator 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 numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));

// 获取子列表(视图,修改会影响原列表)

List 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 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 循环遍历:

// 不推荐:LinkedList循环中使用get(index)

List 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、谨慎使用线性搜索方法

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

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

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

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

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

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