如何在 Java 中检查集合是否为空

JavaJavaBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在这个实验中,你将学习在 Java 中处理集合时如何有效地处理空值。集合是基础的数据结构,编写能够安全处理集合变量可能为 null 的健壮代码至关重要。你将探索如何检查集合是否为 null,如何结合 null 和空值检查以进行全面验证,以及如何利用 Optional 类来增强空值安全性,最终帮助你避免常见的 NullPointerException 错误。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["Object-Oriented and Advanced Concepts"]) java(("Java")) -.-> java/BasicSyntaxGroup(["Basic Syntax"]) java(("Java")) -.-> java/DataStructuresGroup(["Data Structures"]) java/BasicSyntaxGroup -.-> java/if_else("If...Else") java/DataStructuresGroup -.-> java/collections_methods("Collections Methods") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/classes_objects("Classes/Objects") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/exceptions("Exceptions") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/arraylist("ArrayList") subgraph Lab Skills java/if_else -.-> lab-559942{{"如何在 Java 中检查集合是否为空"}} java/collections_methods -.-> lab-559942{{"如何在 Java 中检查集合是否为空"}} java/classes_objects -.-> lab-559942{{"如何在 Java 中检查集合是否为空"}} java/exceptions -.-> lab-559942{{"如何在 Java 中检查集合是否为空"}} java/arraylist -.-> lab-559942{{"如何在 Java 中检查集合是否为空"}} end

检查集合是否为 Null

在这一步中,我们将探讨在 Java 中处理集合时如何处理空值。像 ListSet 这样的集合是基础的数据结构,编写能够安全处理集合变量可能为 null 的代码至关重要。

在 Java 中,null 值意味着变量不引用任何对象。如果你尝试访问 null 对象的方法或属性,程序将会因 NullPointerException 而崩溃。这是 Java 中非常常见的错误,学习如何避免它至关重要。

让我们先创建一个简单的 Java 程序来演示这个问题。

  1. 如果 HelloJava.java 文件尚未在 WebIDE 编辑器中打开,请打开它。

  2. 将文件的全部内容替换为以下代码:

    import java.util.List;
    
    public class HelloJava {
        public static void main(String[] args) {
            List<String> names = null; // Intentionally set to null
    
            // This line will cause a NullPointerException if names is null
            // int size = names.size();
            // System.out.println("Size of the list: " + size);
    
            System.out.println("Program finished.");
        }
    }

    在这段代码中,我们声明了一个名为 names 的字符串列表,并明确将其设置为 null。注释掉的代码行展示了如果我们尝试对 null 列表调用 size() 方法会发生什么 —— 这将导致 NullPointerException

  3. 保存文件(Ctrl+S 或 Cmd+S)。

  4. 现在,让我们编译这个程序。打开 WebIDE 底部的终端,并确保你位于 ~/project 目录下。运行以下命令:

    javac HelloJava.java

    如果编译成功,你应该看不到任何输出。

  5. 现在,运行这个程序:

    java HelloJava

    你应该会看到以下输出:

    Program finished.

    由于会导致 NullPointerException 的代码行被注释掉了,程序运行时不会崩溃。

现在,让我们修改代码,在使用集合之前检查其是否为 null

  1. 再次在编辑器中打开 HelloJava.java

  2. 修改 main 方法以包含空值检查:

    import java.util.List;
    
    public class HelloJava {
        public static void main(String[] args) {
            List<String> names = null; // Intentionally set to null
    
            if (names != null) {
                // This code will only run if names is NOT null
                int size = names.size();
                System.out.println("Size of the list: " + size);
            } else {
                System.out.println("The list is null.");
            }
    
            System.out.println("Program finished.");
        }
    }

    我们添加了一个 if 语句来检查 names 是否不等于 nullnames != null)。获取列表大小并打印的代码现在位于这个 if 块内,这意味着只有当 names 是一个有效的列表对象时,该代码才会执行。else 块则处理 namesnull 的情况。

  3. 保存文件。

  4. 编译修改后的程序:

    javac HelloJava.java
  5. 再次运行程序:

    java HelloJava

    这次,你应该会看到以下输出:

    The list is null.
    Program finished.

    程序正确识别出列表为 null 并打印了相应的消息,避免了 NullPointerException

这个简单的 if (collection != null) 检查是处理集合时防止 NullPointerException 的最基本方法。这是你在 Java 编程中会经常使用的基础技术。

结合空值和空集合检查

在上一步中,我们学习了如何检查集合是否为 null。然而,即使集合不为 null,它也可能是 空的(不包含任何元素)。在很多情况下,你可能希望以相同的方式处理 null 集合和空集合,或者至少处理这两种可能性。

检查集合是否为空可以使用 isEmpty() 方法。如果集合不包含任何元素,该方法返回 true,否则返回 false

让我们修改程序来演示 null 列表和空列表之间的区别,然后将检查结合起来。

  1. 在 WebIDE 编辑器中打开 HelloJava.java 文件。

  2. 将文件内容替换为以下代码:

    import java.util.List;
    import java.util.ArrayList; // Import ArrayList
    
    public class HelloJava {
        public static void main(String[] args) {
            List<String> nullList = null; // Intentionally set to null
            List<String> emptyList = new ArrayList<>(); // An empty list
    
            System.out.println("Checking nullList:");
            if (nullList != null) {
                System.out.println("nullList is not null.");
                if (nullList.isEmpty()) {
                    System.out.println("nullList is empty.");
                } else {
                    System.out.println("nullList is not empty.");
                }
            } else {
                System.out.println("nullList is null.");
            }
    
            System.out.println("\nChecking emptyList:");
            if (emptyList != null) {
                System.out.println("emptyList is not null.");
                if (emptyList.isEmpty()) {
                    System.out.println("emptyList is empty.");
                } else {
                    System.out.println("emptyList is not empty.");
                }
            } else {
                System.out.println("emptyList is null.");
            }
    
            System.out.println("\nProgram finished.");
        }
    }

    我们添加了一个 emptyList,它被初始化为一个新的空 ArrayList。然后,我们对 nullListemptyList 执行相同的空值和空集合检查,以查看输出的差异。

  3. 保存文件。

  4. 在终端中编译程序:

    javac HelloJava.java
  5. 运行程序:

    java HelloJava

    你应该会看到类似以下的输出:

    Checking nullList:
    nullList is null.
    
    Checking emptyList:
    emptyList is not null.
    emptyList is empty.
    
    Program finished.

    这个输出清楚地表明,nullListnull,而 emptyList 不为 null 但为空。

现在,让我们将空值和空集合检查合并为一个条件。常见的做法是检查集合是否为 null 或为空。

  1. 在编辑器中打开 HelloJava.java

  2. 修改 main 方法以合并检查:

    import java.util.List;
    import java.util.ArrayList;
    
    public class HelloJava {
        public static void main(String[] args) {
            List<String> names = null; // Can be null or an empty list
    
            // Combined check: is names null OR is names empty?
            if (names == null || names.isEmpty()) {
                System.out.println("The list is null or empty.");
            } else {
                System.out.println("The list is not null and not empty.");
                // You can safely iterate or access elements here
                // For example:
                // System.out.println("First element: " + names.get(0));
            }
    
            // Let's test with an empty list
            List<String> anotherList = new ArrayList<>();
            System.out.println("\nChecking anotherList (empty):");
            if (anotherList == null || anotherList.isEmpty()) {
                System.out.println("anotherList is null or empty.");
            } else {
                System.out.println("anotherList is not null and not empty.");
            }
    
            // Let's test with a non-empty list
            List<String> populatedList = new ArrayList<>();
            populatedList.add("Item 1");
            System.out.println("\nChecking populatedList (not empty):");
            if (populatedList == null || populatedList.isEmpty()) {
                System.out.println("populatedList is null or empty.");
            } else {
                System.out.println("populatedList is not null and not empty.");
            }
    
    
            System.out.println("\nProgram finished.");
        }
    }

    我们使用逻辑或运算符 (||) 来合并条件 names == nullnames.isEmpty()。如果 任一 条件为真,if 块将执行。我们还添加了对空列表和非空列表的测试,以查看合并检查的行为。

    重要提示: names == null || names.isEmpty() 中条件的顺序至关重要。如果 namesnull,条件的第一部分 (names == null) 为真。由于 Java 中的短路求值,第二部分 (names.isEmpty()) 不会 被求值,从而避免了 NullPointerException。如果你写成 names.isEmpty() || names == null,并且 namesnull,调用 names.isEmpty() 会导致 NullPointerException。在与对象的其他检查结合时,始终要 检查 null

  3. 保存文件。

  4. 编译程序:

    javac HelloJava.java
  5. 运行程序:

    java HelloJava

    你应该会看到类似以下的输出:

    The list is null or empty.
    
    Checking anotherList (empty):
    anotherList is null or empty.
    
    Checking populatedList (not empty):
    populatedList is not null and not empty.
    
    Program finished.

    这展示了合并检查如何正确识别 null 和空列表,并将它们与非空列表区分开来。这种合并检查是处理可能为 null 或为空的集合的一种非常常见且安全的方法。

使用 Optional 确保空值安全

在这一步中,我们将探索一种更现代的方法,即使用 Java 8 引入的 Optional 类来处理 Java 中可能出现的 null 值。Optional 是一个容器对象,它可能包含也可能不包含非 null 值。它提供了一种更明确地表示值是否存在的方式,有助于降低 NullPointerException 的风险。

虽然 if (collection != null) 检查在许多情况下是完全有效的且必要的,但 Optional 可以使你的代码更具可读性和表现力,特别是在处理可能返回值或可能返回 null 的方法时。

让我们看看如何将 Optional 与集合一起使用。尽管 Optional 通常用于单个值,但你可能会遇到方法返回 Optional<List<SomeObject>> 的情况。

  1. 在 WebIDE 编辑器中打开 HelloJava.java 文件。

  2. 将文件内容替换为以下代码,该代码演示了如何将 Optional 与可能为 null 的列表一起使用:

    import java.util.List;
    import java.util.ArrayList;
    import java.util.Optional; // Import Optional
    
    public class HelloJava {
    
        // A method that might return an Optional containing a list, or an empty Optional
        public static Optional<List<String>> getNames(boolean includeNames) {
            if (includeNames) {
                List<String> names = new ArrayList<>();
                names.add("Alice");
                names.add("Bob");
                return Optional.of(names); // Return an Optional containing the list
            } else {
                return Optional.empty(); // Return an empty Optional
            }
        }
    
        public static void main(String[] args) {
            // Case 1: Get names when includeNames is true
            Optional<List<String>> namesOptional1 = getNames(true);
    
            System.out.println("Checking namesOptional1:");
            // Check if the Optional contains a value
            if (namesOptional1.isPresent()) {
                List<String> names = namesOptional1.get(); // Get the list from the Optional
                System.out.println("List is present. Size: " + names.size());
                // You can also check if the list itself is empty
                if (names.isEmpty()) {
                    System.out.println("List is empty.");
                } else {
                    System.out.println("List is not empty. First name: " + names.get(0));
                }
            } else {
                System.out.println("List is not present (Optional is empty).");
            }
    
            System.out.println("---");
    
            // Case 2: Get names when includeNames is false
            Optional<List<String>> namesOptional2 = getNames(false);
    
            System.out.println("Checking namesOptional2:");
            if (namesOptional2.isPresent()) {
                List<String> names = namesOptional2.get();
                System.out.println("List is present. Size: " + names.size());
                if (names.isEmpty()) {
                    System.out.println("List is empty.");
                } else {
                    System.out.println("List is not empty. First name: " + names.get(0));
                }
            } else {
                System.out.println("List is not present (Optional is empty).");
            }
    
    
            System.out.println("\nProgram finished.");
        }
    }

    在这段代码中:

    • 我们定义了一个 getNames 方法,它返回一个 Optional<List<String>>。这个方法模拟了一种场景,即你可能会得到一个列表,也可能什么都得不到(由一个空的 Optional 表示)。
    • main 方法中,我们分别传入 truefalse 调用 getNames 方法来测试这两种情况。
    • 我们使用 namesOptional.isPresent() 来检查 Optional 是否包含列表。
    • 如果 isPresent() 返回 true,我们使用 namesOptional.get() 来获取列表。这是安全的,因为我们已经检查过值是否存在。
    • isPresent() 代码块内,我们可以对列表本身进行检查,例如 names.isEmpty()
  3. 保存文件。

  4. 在终端中编译程序:

    javac HelloJava.java
  5. 运行程序:

    java HelloJava

    你应该会看到类似以下的输出:

    Checking namesOptional1:
    List is present. Size: 2
    List is not empty. First name: Alice
    ---
    Checking namesOptional2:
    List is not present (Optional is empty).
    
    Program finished.

    这个输出展示了 Optional 如何帮助我们处理可能根本不返回列表的情况。

Optional 还提供了其他有用的方法来处理值不存在的情况,例如:

  • orElse(defaultValue):如果值存在则返回该值,否则返回默认值。
  • orElseGet(supplier):如果值存在则返回该值,否则返回 supplier 函数的结果。
  • orElseThrow(exceptionSupplier):如果值存在则返回该值,否则抛出 exceptionSupplier 产生的异常。
  • ifPresent(consumer):如果值存在,则执行给定的操作。

让我们修改代码,使用 ifPresent 以更简洁的方式处理列表存在的情况。

  1. 在编辑器中打开 HelloJava.java

  2. 修改 main 方法以使用 ifPresent

    import java.util.List;
    import java.util.ArrayList;
    import java.util.Optional;
    
    public class HelloJava {
    
        public static Optional<List<String>> getNames(boolean includeNames) {
            if (includeNames) {
                List<String> names = new ArrayList<>();
                names.add("Alice");
                names.add("Bob");
                return Optional.of(names);
            } else {
                return Optional.empty();
            }
        }
    
        public static void main(String[] args) {
            // Case 1: Get names when includeNames is true
            Optional<List<String>> namesOptional1 = getNames(true);
    
            System.out.println("Checking namesOptional1 using ifPresent:");
            namesOptional1.ifPresent(names -> {
                // This block only runs if namesOptional1 contains a list
                System.out.println("List is present. Size: " + names.size());
                if (names.isEmpty()) {
                    System.out.println("List is empty.");
                } else {
                    System.out.println("List is not empty. First name: " + names.get(0));
                }
            });
            if (!namesOptional1.isPresent()) { // Still need a check if you need to handle the absence case
                 System.out.println("List is not present (Optional is empty).");
            }
    
    
            System.out.println("---");
    
            // Case 2: Get names when includeNames is false
            Optional<List<String>> namesOptional2 = getNames(false);
    
            System.out.println("Checking namesOptional2 using ifPresent:");
             namesOptional2.ifPresent(names -> {
                System.out.println("List is present. Size: " + names.size());
                if (names.isEmpty()) {
                    System.out.println("List is empty.");
                } else {
                    System.out.println("List is not empty. First name: " + names.get(0));
                }
            });
             if (!namesOptional2.isPresent()) {
                 System.out.println("List is not present (Optional is empty).");
             }
    
    
            System.out.println("\nProgram finished.");
        }
    }

    我们将 if (namesOptional.isPresent()) { ... namesOptional.get() ... } 结构替换为 namesOptional.ifPresent(names -> { ... })。lambda 表达式 (names -> { ... }) 内的代码仅在 Optional 包含值时执行。我们仍然添加了 if (!namesOptional.isPresent()) 检查来处理 Optional 为空的情况,因为 ifPresent 仅处理值存在的情况。

  3. 保存文件。

  4. 编译程序:

    javac HelloJava.java
  5. 运行程序:

    java HelloJava

    输出应该与之前相同,这表明 ifPresent 提供了另一种处理 Optional 中值存在情况的方式。

使用 Optional 可以使你的代码在值是否可能缺失方面的意图更加清晰,并鼓励你显式地处理这种缺失情况,从而降低意外出现 NullPointerException 的可能性。

总结

在本次实验中,我们学习了如何在 Java 中处理空集合,以防止出现 NullPointerException。我们首先展示了对空集合调用方法会导致运行时错误的问题。

接着,我们探索了不同的技术,以便在尝试访问集合的元素或属性之前,安全地检查集合是否为空。这是编写健壮且无错误的 Java 代码的一项基本技能。