当前位置:首页 > 后端开发 > 正文

java list怎么使用

Java List常用ArrayList,先创建对象,用add()添加元素,get(index)获取,for循环或迭代器遍历

Java中的Listjava.util包下的核心接口之一,用于表示有序可重复元素的集合,它继承了Collection接口,提供了丰富的增删改查操作,并支持通过索引快速访问元素,以下是对其使用方法的全面解析:


核心特性与设计目标

特征 描述
有序性 元素按插入顺序存储,可通过索引定位
可重复性 允许存储相同元素
动态扩容 多数实现类(如ArrayList)会自动扩展底层数组
位置感知 支持get(int index)set(int index, E element)等基于索引的操作
失败快速原则 遍历时若结构被修改(非迭代器自身),会抛出ConcurrentModificationException

主流实现类对比

实现类 底层数据结构 适用场景 关键优势 劣势
ArrayList 动态数组 频繁随机访问/末尾增删 O(1)随机访问
内存连续
中间插入/删除慢(O(n))
LinkedList 双向链表 频繁头尾操作/大量插入/删除 O(1)头尾插入
灵活扩容
随机访问慢(O(n))
Vector 线程安全动态数组 多线程环境(已过时) 同步机制内置 性能低于ArrayList
CopyOnWriteArrayList 写时复制数组 读多写少的并发场景 无锁读操作
避免脏读
写操作开销大

推荐实践:单线程场景优先使用ArrayList;需频繁插入/删除时选择LinkedList;多线程场景建议配合Collections.synchronizedList()包装或使用CopyOnWriteArrayList

java list怎么使用  第1张


核心方法详解

基础操作

import java.util.List;
import java.util.ArrayList;
public class ListDemo {
    public static void main(String[] args) {
        // 1. 创建List实例(推荐使用接口编程)
        List<String> fruits = new ArrayList<>(); // 钻石运算符简化泛型声明
        // 2. 添加元素
        fruits.add("Apple");       // 尾部追加
        fruits.add(0, "Banana");   // 指定位置插入
        fruits.addAll(Arrays.asList("Orange", "Grape")); // 批量添加
        // 3. 访问元素
        System.out.println("第一个元素: " + fruits.get(0)); // 根据索引获取
        System.out.println("所有元素: " + fruits); // toString()自动调用
        // 4. 修改元素
        fruits.set(1, "Mango");    // 替换索引1处的元素
        // 5. 删除元素
        fruits.remove(2);          // 按索引删除
        fruits.remove("Grape");    // 按对象删除(需重写equals方法)
        fruits.clear();            // 清空列表
    }
}

高级操作

方法签名 功能描述 时间复杂度
boolean contains(Object o) 判断是否包含指定元素 O(n)
int indexOf(Object o) 返回首次出现元素的索引(未找到返回-1) O(n)
lastIndexOf(Object o) 返回最后一次出现的索引 O(n)
List<E> subList(int from, int to) 返回指定区间的子视图(原地修改会影响原列表) O(1)
void sort(Comparator<? super E> c) 自然排序或自定义排序 O(n log n)
E[] toArray() 转换为数组 O(n)

注意subList()返回的是原列表的视图,对子列表的修改会直接影响原列表。


典型使用场景示例

场景1:统计学生成绩排名

List<Integer> scores = new ArrayList<>(Arrays.asList(85, 92, 78, 90, 88));
Collections.sort(scores, Comparator.reverseOrder()); // 降序排序
System.out.println("前三名: " + scores.subList(0, 3)); // [92, 90, 88]

场景2:过滤偶数

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
numbers.removeIf(n -> n % 2 == 0); // 删除所有偶数 → [1, 3, 5]

场景3:批量替换元素

List<String> words = new ArrayList<>(Arrays.asList("cat", "dog", "fish"));
for (int i = 0; i < words.size(); i++) {
    if (words.get(i).length() > 3) {
        words.set(i, words.get(i).toUpperCase()); // ["CAT", "DOG", "FISH"]
    }
}

性能优化技巧

  1. 预设初始容量:已知数据量时通过构造函数指定初始容量,避免多次扩容。
    // 预估存储1000个元素,减少扩容次数
    List<String> largeList = new ArrayList<>(1000);
  2. 避免频繁插入中间位置ArrayListadd(index, element)会导致后续元素整体后移,时间复杂度为O(n),若需高频插入,改用LinkedList
  3. 慎用contains判断存在性:对于大型列表,改用HashSet进行存在性校验更高效。
  4. 遍历方式选择
    • 普通for循环:适合ArrayList(内存局部性更好)
    • 增强for循环:代码简洁,但无法在遍历中删除元素
    • Iterator:唯一支持遍历中安全删除的方式
      Iterator<String> iter = fruits.iterator();
      while (iter.hasNext()) {
          String fruit = iter.next();
          if (fruit.startsWith("A")) {
              iter.remove(); // 安全删除
          }
      }

常见误区与解决方案

误区 后果 解决方案
直接使用原始类型(未指定泛型) 编译警告+类型转换异常 始终使用泛型参数
认为Vector是线程安全的替代品 实际仍需要额外同步措施 使用Collections.synchronizedList()CopyOnWriteArrayList
在遍历时调用remove() ConcurrentModificationException 使用Iterator.remove()或先收集待删除元素再统一处理
忽略subList()的关联性 意外修改原列表 若需独立副本,调用new ArrayList<>(subList)
错误比较对象(如自定义对象未重写equals contains/indexOf失效 确保元素类正确实现hashCode()equals()

相关问答FAQs

Q1: 如何选择ArrayList还是LinkedList

A: 根据操作类型决定:

  • 优先选ArrayList:如果业务以随机访问为主(如按索引查询),或数据量较小且增长平稳,其内存连续布局带来更好的缓存命中率。
  • 优先选LinkedList:如果业务以频繁插入/删除操作为主(尤其是中间位置),或数据量极大且稀疏,链表结构在这些场景下效率更高。
  • 特殊场景:若既需要快速随机访问又需要频繁插入/删除,可考虑第三方库如Apache Commons Lang的FastArrayList

Q2: 为什么不能直接遍历时调用list.remove(element)

A: 因为标准for-each循环和普通for循环在遍历时,如果通过list.remove()直接修改列表结构,会导致预期外的ConcurrentModificationException,这是因为Java集合框架采用失败快速机制(Fail-Fast):遍历时会记录modCount(修改次数计数器),当检测到结构被非规修改时立即抛出异常,正确的删除方式有两种:

  1. 使用迭代器的remove()方法:这是唯一安全的遍历中删除方式。
  2. 先标记后删除:先收集所有待删除元素的索引或引用,遍历结束后
0