Java List集合的用法(非常详细,附带实例)
下图所示是一个字符 List 集合,这个集合中有 5 个元素,元素索引从 0 开始。

图 1 字符List集合
List 集合关心元素是否有序,而不关心是否重复,请大家记住这个原则。例如,下图所示字符 List 集合中就有两个字符 'l'。
Java List接口的实现类

图 2 Java中主要的集合接口和类图
从图 2 可见,Java 中描述 List 集合的接口是 List,它的实现类主要有 ArrayList 和 LinkedList:
- ArrayList 是基于动态数组数据结构的实现;
- LinkedList 是基于链表数据结构的实现。
ArrayList 访问元素的速度优于 LinkedList,LinkedList 占用的内存空间比较大,但 LinkedList 在批量插入或删除数据时优于 ArrayList。
在软件开发中,经常会面临空间和时间的权衡。不同的算法和数据结构在实现上会有不同的优势和劣势,程序员需要根据具体的需求和场景来选择合适的数据结构和算法。
Java List接口的常用方法
List 接口继承自 Collection 接口,List 接口中的很多方法都是继承自 Collection 接口的。下面介绍几个 List 接口的常用方法。
1) 操作元素
方法 | 功能 |
---|---|
get(int index) | 返回List集合中指定位置的元素。 |
set(int index, Object element) | 用指定元素替换List集合中指定位置的元素。 |
add(Object element) | 在List集合的尾部添加指定的元素。该方法是从Collection接口继承过来的。 |
add(int index, Object element) | 在List集合的指定位置插入指定元素。 |
remove(int index) | 移除List集合中指定位置的元素。 |
remove(Object element) | 如果List集合中存在指定元素,则从List集合中移除第一次出现的指定元素。该方法是从Collection接口继承过来的。 |
clear() | 从List集合中移除所有元素。该方法是从Collection接口继承过来的。 |
2) 判断元素
方法 | 功能 |
---|---|
isEmpty() | 判断List集合中是否有元素,如果没有,返回true,如果有,返回false。该方法是从Collection接口继承过来的。 |
contains(Object element) | 判断List集合中是否包含指定元素,如果包含,返回true,如果不包含,返回false。该方法是从Collection接口继承过来的。 |
3) 查询元素
方法 | 功能 |
---|---|
indexOf(Object o) | 从前往后查找List集合元素,返回第一次出现指定元素的索引,如果此列表不包含该元素,则返回-1。 |
lastIndexOf(Object o) | 从后往前查找List集合元素,返回第一次出现指定元素的索引,如果此列表不包含该元素,则返回-1。 |
4) 遍历集合
方法 | 功能 |
---|---|
forEach() | 遍历集合。 |
5) 其他
方法 | 功能 |
---|---|
iterator() | 返回迭代器(Iterator)对象,迭代器对象用于遍历集合。该方法是从Collection接口继承过来的。 |
size() | 返回List集合中的元素数,返回值是int类型的。该方法是从Collection接口继承过来的。 |
subList(int fromIndex, int toIndex) | 返回List集合中指定的fromIndex(包括)和toIndex(不包括)之间的元素集合,返回值为List集合。 |
示例代码如下:
import java.util.ArrayList; // 引入ArrayList类 import java.util.List; // 引入List接口 public class Main { public static void main(String args[]) { List list; // 声明变量 list 为 List 接口类型 list = new ArrayList(); // 实例化ArrayList对象 String str = "Hello"; // 声明字符串 str for (int i = 0; i < str.length(); i++) { // 变量字符串 str list.add(str.charAt(i)); // 从集合字符串中取值字符,并添加到变量 list 中 } System.out.println(list); // 打印到变量 list System.out.println(list.get(5)); // ① 访问元素发生 IndexOutOfBoundsException 异常 } }程序的运行结果如下:
[H, e, l, l, o]
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 5 out of bounds for length 5
at demo.Main.main(Main.java:14)
使用泛型
集合中可以保存任何对象,但有时候需要保证放入的数据类型与取出的数据类型保持一致,否则可能会发生异常。先看看如下代码:
import java.util.LinkedList; import java.util.List; public class MainWithoutGenerics { public static void main(String args[]) { List list; // 声明变量 list 为 List 接口类型 list = new LinkedList(); // 实例化LinkedList对象 // 向集合中添加元素 list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); // 遍历集合 for (Object item : list) { Integer element = (Integer) item; // ① 发生 ClassCastException 异常 System.out.println("读取集合元素: " + element); } } }上述代码运行时发生异常,运行结果如下:
Exception in thread "main" java.lang.ClassCastException:
at demo.MainWithoutGenerics.main(MainWithoutGenerics.java:15)
在代码第 ① 处需要进行强制类型转换。强制类型转换是有风险的,如果不进行判断就进行类型转换,则会发生 ClassCastException 异常。
在 Java 5 之前没有好的解决办法,在类型转换之前要通过 instanceof 运算符判断该对象是否是目标类型。而泛型的引入可以将这些运行时异常提前到编译期暴露出来,这增强了类型安全检查。
修改程序代码如下:
import java.util.LinkedList; import java.util.List; public class NonGenericDemo { public static void main(String args[]) { List list; // 声明变量 list 为 List 接口类型 list = new LinkedList(); // 实例化 LinkedList 对象 // 向集合中添加元素 list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); // 遍历集合 for (Object item : list) { if (item instanceof Integer) { // 测试 item 的类型是否为 Integer Integer element = (Integer) item; System.out.println("读取集合元素: " + element); } } } }
Java 5 之后所有的集合类型都可以有泛型类型,可以限定存放到集合中的类型,修改程序代码如下:
import java.util.LinkedList; import java.util.List; public class GenericDemo { public static void main(String args[]) { List<String> list; // ① 声明变量 list 为 List 接口类型 list = new LinkedList<String>(); // ② 实例化 ArrayList 对象 // 向集合中添加元素 list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); list.add(6); // ③ 无法添加非字符串类型,发生编译错误 // 遍历集合 for (String item : list) { Integer element = (Integer) item; // ④ 发生编译错误 System.out.println("读取集合元素: " + item); } } }上述代码第 ① 处声明数据类型时在 List 后面添加了
<String>
。代码第 ② 处在实例化时需要使用
LinkedList<String>
形式,从 Java 9 之后可以省略 ArrayList 的尖括号中的数据类型,即采用 LinkedList<> 的形式。List 和 ArrayList 就是泛型表示方式,尖括号中可以是任何的引用类型,它限定了集合中是否能存放该种类型的对象,所以代码第 ③ 处试图添加非 String 类型元素时,会发生编译错误。
如果在代码第 ④ 处试图转换为 Integer,则会发生编译错误。可见原本在运行时发生的异常,提早暴露到编译期,使程序员能及早发现问题,避免程序发布上线之后发生系统崩溃的情况。
遍历List集合
集合最常用的操作之一是遍历,遍历就是将集合中的每一个元素取出来进行操作或计算。有三种方法遍历 List 集合:- 使用 C语言风格 for 循环(传统 for 循环)遍历。List 集合可以使用 for 循环进行遍历,for 循环中有循环变量,通过循环变量可以访问 List 集合中的元素。
- 使用 Java 风格 for 循环(增强 for 循环)遍历。增强 for 循环是针对遍历各种类型的集合而推出的,推荐使用这种遍历方法。
- 使用 forEach() 方法遍历。
示例代码如下:
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String args[]) { List<String> list; // 声明变量 list 为 List 接口类型 list = new ArrayList<>(); // 实例化 ArrayList 对象 // 向集合中添加元素 list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); // 1.使用 C 语言风格 for 循环遍历 System.out.println("--1.使用 C 语言风格 for 循环遍历--"); for (int i = 0; i < list.size(); i++) { System.out.printf("读取集合元素(%d): %s \n", i, list.get(i)); } // 2.使用 Java 风格 for 循环遍历 System.out.println("--2.使用 Java 风格 for 循环遍历--"); for (String item : list) { System.out.println("读取集合元素: " + item); } // 3.使用 forEach 方法循环遍历 System.out.println("--3.使用 forEach 方法循环遍历--"); list.forEach(item -> { // ① System.out.println("读取集合元素: " + item); }); } }程序的运行结果如下:
--1.使用 C 语言风格 for 循环遍历--
读取集合元素(0): 1
读取集合元素(1): 2
读取集合元素(2): 3
读取集合元素(3): 4
读取集合元素(4): 5
--2.使用 Java 风格 for 循环遍历--
读取集合元素: 1
读取集合元素: 2
读取集合元素: 3
读取集合元素: 4
读取集合元素: 5
--3.使用 forEach 方法循环遍历--
读取集合元素: 1
读取集合元素: 2
读取集合元素: 3
读取集合元素: 4
读取集合元素: 5
相关文章
- Java List集合:ArrayList和LinkedList类的用法及区别
- Java List.add()方法:向集合列表中添加对象
- Java List.isEmpty()方法:判断集合对象是否为空
- Java List.addAll()方法:添加所有元素到列表中
- Java List.contains()方法:判断列表中是否包含指定元素
- Java List.get()方法:获取列表指定位置的元素
- Java List.remove()方法:移出列表中的指定元素
- Java List.removeAll()方法:从列表中移除所有元素
- Java List.size()方法:返回列表中元素的个数
- Java List.subList()方法:获取列表中指定范围的子列表