Как проверить, является ли коллекция в Java null

JavaJavaBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии (лабораторной работе) вы научитесь эффективно обрабатывать нулевые значения (null values) при работе с коллекциями в Java. Коллекции являются фундаментальными структурами данных, и крайне важно писать надежный код, который может безопасно управлять ситуациями, когда переменная коллекции может быть равна null. Вы узнаете, как проверять коллекции на null, комбинировать проверки на null и пустоту для комплексной валидации, а также использовать класс Optional для повышения безопасности при работе с null. Это поможет вам избежать распространенных ошибок NullPointerException.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("Java")) -.-> java/BasicSyntaxGroup(["Basic Syntax"]) java(("Java")) -.-> java/DataStructuresGroup(["Data Structures"]) java(("Java")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["Object-Oriented and Advanced Concepts"]) 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 null"}} java/collections_methods -.-> lab-559942{{"Как проверить, является ли коллекция в Java null"}} java/classes_objects -.-> lab-559942{{"Как проверить, является ли коллекция в Java null"}} java/exceptions -.-> lab-559942{{"Как проверить, является ли коллекция в Java null"}} java/arraylist -.-> lab-559942{{"Как проверить, является ли коллекция в Java null"}} end

Проверка коллекции на null

На этом этапе мы рассмотрим, как обрабатывать нулевые значения (null values) при работе с коллекциями в Java. Коллекции, такие как List или Set, являются фундаментальными структурами данных, и крайне важно писать код, который может безопасно обрабатывать ситуации, когда переменная коллекции может быть равна 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. Закомментированные строки показывают, что произойдет, если мы попытаемся вызвать метод size() для списка, равного null – это вызовет ошибку NullPointerException.

  3. Сохраните файл (Ctrl+S или Cmd+S).

  4. Теперь скомпилируем программу. Откройте терминал внизу WebIDE и убедитесь, что вы находитесь в директории ~/project. Выполните следующую команду:

    javac HelloJava.java

    Если компиляция прошла успешно, вы не должны увидеть никакого вывода.

  5. Теперь запустите программу:

    java HelloJava

    Вы должны увидеть следующий вывод:

    Program finished.

    Поскольку строка, которая вызывала бы ошибку NullPointerException, закомментирована, программа запускается без аварийного завершения.

Теперь изменим код, чтобы проверить на null перед использованием коллекции.

  1. Откройте файл HelloJava.java снова в редакторе.

  2. Измените метод main, добавив проверку на null:

    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 значению null (names != null). Код для получения размера списка и его вывода теперь находится внутри этого блока if, что означает, что он будет выполняться только в том случае, если names является действительным объектом списка. Блок else обрабатывает случай, когда names равен null.

  3. Сохраните файл.

  4. Скомпилируйте модифицированную программу:

    javac HelloJava.java
  5. Запустите программу снова:

    java HelloJava

    На этот раз вы должны увидеть следующий вывод:

    The list is null.
    Program finished.

    Программа правильно определила, что список был равен null, и вывела соответствующее сообщение, избежав ошибки NullPointerException.

Эта простая проверка if (collection != null) является самым базовым способом предотвращения ошибки NullPointerException при работе с коллекциями. Это фундаментальная техника, которую вы будете часто использовать в Java-программировании.

Комбинирование проверок на null и пустоту

На предыдущем этапе мы узнали, как проверить, является ли коллекция равной null. Однако коллекция может быть пустой (не содержать элементов), даже если она не равна null. Во многих случаях вы, возможно, захотите обрабатывать null-коллекцию и пустую коллекцию одинаково или, по крайней мере, предусмотреть оба варианта.

Проверка коллекции на пустоту выполняется с помощью метода isEmpty(). Этот метод возвращает true, если коллекция не содержит элементов, и false в противном случае.

Давайте изменим нашу программу, чтобы продемонстрировать разницу между null-списком и пустым списком, а затем объединим проверки.

  1. Откройте файл HelloJava.java в редакторе WebIDE.

  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. Затем мы выполняем одинаковые проверки на null и пустоту для nullList и emptyList, чтобы увидеть разницу в выводе.

  3. Сохраните файл.

  4. Скомпилируйте программу в терминале:

    javac HelloJava.java
  5. Запустите программу:

    java HelloJava

    Вы должны увидеть вывод, похожий на следующий:

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

    Этот вывод четко показывает, что nullList равен null, в то время как emptyList не равен null, но пуст.

Теперь объединим проверки на 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 == null и names.isEmpty(). Блок if будет выполняться, если любое из условий истинно. Мы также добавили тесты с пустым списком и заполненным списком, чтобы увидеть, как работает объединенная проверка.

    Важное примечание: Порядок условий в names == null || names.isEmpty() имеет решающее значение. Если names равен null, первая часть условия (names == null) истинна. Из-за короткого замыкания в Java вторая часть (names.isEmpty()) не вычисляется, что предотвращает ошибку NullPointerException. Если бы вы написали names.isEmpty() || names == null, и names был равен null, вызов 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 для безопасности при работе с null

На этом этапе мы рассмотрим более современный подход к обработке потенциальных значений null в Java с использованием класса Optional, введенного в Java 8. Optional представляет собой контейнерный объект, который может содержать или не содержать не-null значение. Он предоставляет способ более явно представлять наличие или отсутствие значения, что может помочь уменьшить риск возникновения ошибок NullPointerException.

Хотя проверки if (collection != null) вполне допустимы и необходимы во многих ситуациях, использование Optional может сделать ваш код более читаемым и выразительным, особенно при работе с методами, которые могут вернуть значение или null.

Давайте посмотрим, как можно использовать Optional с коллекцией. Хотя Optional обычно используется для одиночных значений, вы можете столкнуться с ситуациями, когда метод возвращает Optional<List<SomeObject>>.

  1. Откройте файл HelloJava.java в редакторе WebIDE.

  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 мы вызываем getNames с параметрами true и false, чтобы протестировать оба случая.
    • Мы используем 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 -> { ... }). Код внутри лямбда-выражения (names -> { ... }) будет выполняться только в том случае, если Optional содержит значение. Мы все еще добавили проверку if (!namesOptional.isPresent()) для обработки случая, когда Optional пуст, так как ifPresent обрабатывает только случай наличия значения.

  3. Сохраните файл.

  4. Скомпилируйте программу:

    javac HelloJava.java
  5. Запустите программу:

    java HelloJava

    Вывод должен быть таким же, как и раньше, что демонстрирует, что ifPresent предоставляет альтернативный способ обработки наличия значения в Optional.

Использование Optional может сделать намерения вашего кода более ясными в отношении того, может ли значение отсутствовать, и поощряет вас явно обрабатывать такое отсутствие, уменьшая вероятность непредвиденных ошибок NullPointerException.

Резюме

В этом практическом занятии (лабораторной работе) мы научились обрабатывать null-коллекции в Java, чтобы предотвратить ошибку NullPointerException. Мы начали с демонстрации проблемы вызова методов для null-коллекции, которая приводит к ошибке во время выполнения программы.

Затем мы рассмотрели различные методы для безопасной проверки, является ли коллекция null, прежде чем пытаться получить доступ к ее элементам или свойствам. Это фундаментальный навык для написания надежного и безошибочного кода на Java.