Java集合——2.使用List
Java List接口
在Java集合框架中,List是最常用的接口之一,它代表了一个有序可重复的元素集合。与数组相比,List提供了动态调整大小、丰富的操作方法等优势,成为处理有序数据的首选工具。
List接口的核心特性
List继承自Collection接口,它的核心特点是:
- 有序性:元素按照插入顺序存储,每个元素都有对应的索引位置(从0开始)
- 可重复性:允许存储重复的元素(通过
equals()方法判断) - 动态大小:容量会根据元素数量自动调整
- 随机访问:可以通过索引直接访问任意元素
List接口定义了一系列核心方法,奠定了所有实现类的行为规范:
| 方法 | 功能描述 |
|---|---|
boolean add(E e) |
在列表末尾添加元素 |
void add(int index, E element) |
在指定索引位置插入元素 |
boolean remove(Object o) |
删除指定元素 |
E remove(int index) |
删除指定索引的元素 |
E set(int index, E element) |
替换指定索引的元素 |
E get(int index) |
获取指定索引的元素 |
int indexOf(Object o) |
返回指定元素首次出现的索引 |
boolean contains(Object o) |
判断列表是否包含指定元素 |
int size() |
返回列表中的元素数量 |
Iterator<E> iterator() |
返回遍历元素的迭代器 |
List的主要实现类
Java提供了多个List接口的实现类,其中最常用的是ArrayList和LinkedList,它们各有优缺点,适用于不同场景。
1. ArrayList:基于动态数组的实现
ArrayList是List接口的主要实现类,它在内部使用动态数组存储元素,具有以下特点:
内部工作原理
ArrayList维护了一个可变长度的数组,当元素数量超过当前容量时,会自动创建一个更大的数组(通常是原容量的1.5倍),并将原有元素复制到新数组中:
// ArrayList扩容过程简化逻辑
public boolean add(E e) {
if (size == capacity) {
// 创建新数组,容量为原容量的1.5倍
E[] newArray = new E[capacity * 3 / 2];
// 复制旧数组元素到新数组
System.arraycopy(oldArray, 0, newArray, 0, size);
oldArray = newArray;
capacity = newArray.length;
}
// 添加新元素
oldArray[size++] = e;
return true;
}
优缺点分析
优点:
- 随机访问效率高(通过索引访问,时间复杂度O(1))
- 尾部添加元素效率高(通常为O(1))
- 内存占用相对较少
缺点:
- 中间插入或删除元素效率低(需要移动大量元素,时间复杂度O(n))
- 扩容时需要复制数组,可能产生性能开销
使用示例
import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
public static void main(String[] args) {
// 创建存储String类型的ArrayList
List<String> fruits = new ArrayList<>();
// 添加元素
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橙子");
// 在指定位置插入元素
fruits.add(1, "葡萄");
// 获取元素
System.out.println("索引2的元素:" + fruits.get(2));
// 修改元素
fruits.set(0, "红苹果");
// 遍历元素
for (String fruit : fruits) {
System.out.println(fruit);
}
// 删除元素
fruits.remove(2);
System.out.println("最终列表:" + fruits);
}
}
2. LinkedList:基于双向链表的实现
LinkedList通过双向链表实现List接口,每个元素包含前驱和后继引用,形成链式结构。
优缺点分析
优点:
- 中间插入和删除元素效率高(只需修改前后元素的引用,时间复杂度O(1))
- 不需要预先分配容量,内存利用率高
缺点:
- 随机访问效率低(需要从头遍历,时间复杂度O(n))
- 每个元素需要额外存储前后引用,内存占用较大
使用场景
LinkedList适合以下场景:
- 频繁在中间位置插入或删除元素
- 元素数量不确定,且不需要频繁随机访问
- 需要实现队列、栈等数据结构(
LinkedList同时实现了Deque接口)
ArrayList与LinkedList的性能对比
| 操作 | ArrayList | LinkedList | 推荐选择 |
|---|---|---|---|
| 随机访问(get/set) | 快(O(1)) | 慢(O(n)) | ArrayList |
| 尾部添加(add) | 快(O(1)) | 快(O(1)) | 两者均可 |
| 中间插入/删除 | 慢(O(n)) | 快(O(1)) | LinkedList |
| 内存占用 | 较少 | 较多 | ArrayList |
| 迭代遍历 | 快 | 较快 | 差异不大 |
最佳实践:在大多数情况下,优先选择ArrayList,它在绝大多数场景下性能更优。只有当需要频繁在中间位置进行插入/删除操作时,才考虑使用LinkedList。
List的创建与初始化
注:ArrayList 类位于 java.util 包中,使用前需要引入它import java.util.ArrayList;
Java提供了多种创建List的方式,适用于不同场景:
1. 使用实现类的构造方法
// 创建空列表
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new LinkedList<>();
// 创建时指定初始容量(仅ArrayList有效)
List<String> list3 = new ArrayList<>(100); // 初始容量100
2. 使用List.of()方法(Java 9+)
创建包含指定元素的列表:
String[] arr = {"苹果", "香蕉", "橙子"};
List<String> list = List.of(arr);//数组转列表
List<Integer> numbers = List.of(1, 2, 3, 4);
注意:
List.of()返回的列表不能执行add、remove、set等修改操作- 不允许包含
null元素,否则会抛出NullPointerException(ArrayList中null视为一个元素) - 创建可支持
add、remove和set操作的ArrayList,可以使用new ArrayList<>(List.of(array))
3. 使用Arrays.asList()方法
将数组转换为列表:
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
注意:
- Arrays.asList(array)返回的列表是数组的视图,修改列表会影响原数组
- 不支持
add和remove操作(会抛出UnsupportedOperationException),但支持set操作 - 允许包含
null元素 - 创建可支持
add、remove和set操作的ArrayList,可以使用new ArrayList<>(Arrays.asList(array))
List的遍历方式
遍历List有多种方式,选择合适的方式能提高代码效率和可读性:
1. 增强for循环(推荐)
最简洁的遍历方式,内部使用迭代器实现:
List<String> fruits = new ArrayList<>(List.of("苹果", "香蕉", "橙子"));
for (String fruit : fruits) {
System.out.println(fruit);
}
2. 迭代器(Iterator)
适合需要在遍历中删除元素的场景:
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if (fruit.equals("香蕉")) {
iterator.remove(); // 安全删除当前元素
}
}
3. 普通for循环(按索引)
适合需要索引的场景,但对LinkedList效率较低:
for (int i = 0; i < fruits.size(); i++) {
System.out.println("索引" + i + ":" + fruits.get(i));
}
List与数组的相互转换
在实际开发中,经常需要在List和数组之间进行转换:
1. List转换为数组
List<String> fruits = new ArrayList<>(List.of("苹果", "香蕉", "橙子"));
// 方法1:默认返回Object[](不推荐)
Object[] objArray = fruits.toArray();
// 方法2:指定类型(推荐):传入一个长度为0的String数组作为参数,Java会根据list的实际大小创建一个新的String数组并返回。
String[] strArray1 = fruits.toArray(new String[0]);
// 方法3:使用方法引用(Java 11+,推荐)
String[] strArray2 = fruits.toArray(String[]::new);
2. 数组转换为List
使用之前讲解过的List.of()和Arrays.asList()方法:
String[] array = {"a", "b", "c"};
// 方法1:返回不可修改的列表(Java 9+)
List<String> list1 = List.of(array);
// 方法2:返回可修改的列表
List<String> list2 = new ArrayList<>(List.of(array));
// 方法3:返回可修改的列表
List<String> list3 = new ArrayList<>(Arrays.asList(array));
其他常用操作
1. 批量添加元素
List<String> list = new ArrayList<>();
list.addAll(List.of("a", "b", "c")); // 添加另一个集合的所有元素
list.addAll(1,List.of("a", "b", "c"));// 设置在指定索引批量插入元素
2. 列表排序
List<Integer> numbers = new ArrayList<>(List.of(3, 1, 4, 2));
Collections.sort(numbers); // 升序排序
System.out.println(numbers); // [1, 2, 3, 4]
// 自定义排序(降序)
numbers.sort(Collections.reverseOrder());
System.out.println(numbers); // [4, 3, 2, 1]
3. 列表去重
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedHashSet;
List<String> withDuplicates = new ArrayList<>(List.of("a", "b", "a", "c"));
List<String> withoutDuplicates = new ArrayList<>(new LinkedHashSet<>(withDuplicates));
4. 子列表操作
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5));
List<Integer> subList = numbers.subList(1, 4); // 包含索引1,不包含索引4
System.out.println(subList); // [2, 3, 4]
// 对子列表的修改会影响原列表
subList.set(0, 99);
System.out.println(numbers); // [1, 99, 3, 4, 5]
5. 转为字符串
ArrayList<String> sites = new ArrayList<>();
sites.add("Runoob");
sites.add("Google");
sites.add("Wiki");
sites.add("Taobao");
System.out.println("网站列表: " + sites);
// 将ArrayList转换为String类型
String list = sites.toString();
System.out.println("String: " + list);
System.out.println("第一个字符: " + list.charAt(0));
输出:
网站列表: [Runoob, Google, Wiki, Taobao]
String: [Runoob, Google, Wiki, Taobao]
第一个字符: [
总结
List接口的实现类ArrayList和LinkedList各有侧重- 掌握
List接口的常用的增、删、改、查和遍历方法 - 掌握
List的初始化构建方式 - 掌握
List与数组的相互转换方法,需注意List.of()和Arrays.asList()返回的区别 - 掌握其他常用操作,如批量添加元素、排序、去重、子列表操作等
Java集合——2.使用List的更多相关文章
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- Scala集合和Java集合对应转换关系
作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 用Scala编码的时候,经常会遇到scala集合和Java集合互相转换的case,特意mark一 ...
- java集合你了解多少?
用了java集合这么久,还没有系统的研究过java的集合结构,今天亲自画了下类图,总算有所收获. 一.所有集合都实现了Iterable接口. Iterable接口中包含一个抽象方法:Iterator& ...
- 深入java集合学习1-集合框架浅析
前言 集合是一种数据结构,在编程中是非常重要的.好的程序就是好的数据结构+好的算法.java中为我们实现了曾经在大学学过的数据结构与算法中提到的一些数据结构.如顺序表,链表,栈和堆等.Java 集合框 ...
- Java集合框架List,Map,Set等全面介绍
Java集合框架的基本接口/类层次结构: java.util.Collection [I]+--java.util.List [I] +--java.util.ArrayList [C] +- ...
- Java集合框架练习-计算表达式的值
最近在看<算法>这本书,正好看到一个计算表达式的问题,于是就打算写一下,也正好熟悉一下Java集合框架的使用,大致测试了一下,没啥问题. import java.util.*; /* * ...
- 【集合框架】Java集合框架综述
一.前言 现笔者打算做关于Java集合框架的教程,具体是打算分析Java源码,因为平时在写程序的过程中用Java集合特别频繁,但是对于里面一些具体的原理还没有进行很好的梳理,所以拟从源码的角度去熟悉梳 ...
- Java 集合框架
Java集合框架大致可以分为五个部分:List列表,Set集合.Map映射.迭代器.工具类 List 接口通常表示一个列表(数组.队列.链表 栈),其中的元素 可以重复 的是:ArrayList 和L ...
- Java集合概述
容器,是用来装东西的,在Java里,东西就是对象,而装对象并不是把真正的对象放进去,而是指保存对象的引用.要注意对象的引用和对象的关系,下面的例子说明了对象和对象引用的关系. String str = ...
- 深入java集合系列文章
搞懂java的相关集合实现原理,对技术上有很大的提高,网上有一系列文章对java中的集合做了深入的分析, 先转载记录下 深入Java集合学习系列 Java 集合系列目录(Category) HashM ...
随机推荐
- 1.Windows Server 2012 R2安装.NET Framework4.7.1
1.KB2919442 https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=42153 2.clearcompressionfl ...
- Gitee、Github上star星星数获取到一个图片里,用于MD文档
记录一下 Gitee 用这个链接当图片地址即可 https://gitee.com/用户名/仓库名/badge/star.svg?theme=white https://gitee.com/用户名/仓 ...
- gRPC-go的一些tips
1.请注意,在 gRPC-Go 中,RPC 以阻塞/同步模式运行,这意味着 RPC 调用等待服务器响应,并且将返回响应或错误. Note that in gRPC-Go, RPCs operate i ...
- 🚀 开源提示词优化神器来了!一键优化Function Calling和MCP提示词,让你的AI应用性能飞跃
还在为Function Calling调用不准确而头疼?MCP提示词写得不够规范?今天给大家推荐一个开源的提示词优化平台,专门解决这些痛点! 背景:为什么需要专业的提示词优化? 在AI应用开发中,我们 ...
- cookie,session以及application的比较
cookie工作原理: cookie代码: 1 @WebServlet(value = "/cookie",name = "CookieServlet") 2 ...
- What is Good Code
DRY:Don't repeat yourself 尽可能的不要有重复代码,同时意味着你要有足够多的抽象和思考,将相同的功能函数放在一个地方,不要重复. SRP:Single responsiblit ...
- react项目添加typescript类型定义文件 .d.ts
最近用react + antd mobile + typescript开发项目,其中使用了rc-form这个包,可惜没有typescript版本,导致找不到类型定义. 一起来重温一下这个经典的错误. ...
- Excel Micro (VBA)
- 插件报错:SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“.解决方案
今天在用Maven搞一个工程,安装要求我添加了所有需要的依赖,可是一运行测试程序,就跳出这样一个大大的错误: SLF4J: Failed to load class "org.slf4j.i ...
- Jfinal启动报错-文件找不到