首页 > 编程笔记 > Java笔记 阅读:22

Java List集合的用法(非常详细,附带实例)

Java 中的 List 集合类似于字符串或数组,其中的元素是有序的。

下图所示是一个字符 List 集合,这个集合中有 5 个元素,元素索引从 0 开始。


图 1 字符List集合

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

Java List接口的实现类


图 2 Java中主要的集合接口和类图

从图 2 可见,Java 中描述 List 集合的接口是 List,它的实现类主要有 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)

上述代码第 ① 处获取 list 中的元素,由于索引超出范围,导致 IndexOutOfBoundsException 异常。

使用泛型

集合中可以保存任何对象,但有时候需要保证放入的数据类型与取出的数据类型保持一致,否则可能会发生异常。

先看看如下代码:
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)

上述代码实现的功能很简单,就是将一些数据保存到集合中,然后再取出。但对于使用 Java 5 之前版本的程序员而言,使用集合时经常会面临一个很尴尬的问题:放入一种特定类型,但是取出时全部是 Object 类型,于是在具体使用时需要将元素转换为特定类型。

在代码第 ① 处需要进行强制类型转换。强制类型转换是有风险的,如果不进行判断就进行类型转换,则会发生 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 集合:
  1. 使用 C语言风格 for 循环(传统 for 循环)遍历。List 集合可以使用 for 循环进行遍历,for 循环中有循环变量,通过循环变量可以访问 List 集合中的元素。
  2. 使用 Java 风格 for 循环(增强 for 循环)遍历。增强 for 循环是针对遍历各种类型的集合而推出的,推荐使用这种遍历方法。
  3. 使用 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

其他两个遍历方法比较简单,不再赘述,这里重点介绍 forEach() 方法,见代码第 ① 处,其中 forEach() 方法用来遍历集合并对每个元素执行特定操作,其中 item 参数就是从 List 集合中取出的每一个元素。

相关文章