Avatar ·

Как объединить два списка (List) в Java? Простые и эффективные способы

Всем привет! Я новичок в Java, и у меня возникла, казалось бы, простая задача. У меня есть два списка (ArrayList), которые содержат строки. Например, первый список — это имена сотрудников из отдела А, а второй — имена из отдела Б. Мне нужно объединить их в один общий список для дальнейшей обработки (например, для вывода на экран или передачи в другой метод). Вот как я пытался это сделать: ```java import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List listA = new ArrayList<>(); listA.add("Alice"); listA.add("Bob"); List listB = new ArrayList<>(); listB.add("Charlie"); listB.add("David"); // Пытаюсь объединить, но компилятор ругается // List mergedList = listA + listB; // Ошибка! Оператор + не работает со списками } } ``` Я понимаю, что в Java нет оператора `+` для списков, как, например, в Python или JavaScript. Я перепробовал несколько подходов: создавал новый список и в цикле добавлял элементы из обоих списков, но это выглядит громоздко. Также я слышал про метод `addAll()`, но не уверен, что он делает копию элементов или просто ссылается на исходные списки. Подскажите, пожалуйста, как правильно и красиво объединить два списка в Java, желательно без лишних циклов и с учётом того, что Java 5 (или новее). Мне важно, чтобы исходные списки остались неизменными. Заранее спасибо! <<>>

Использование метода addAll() — самый простой способ

Ваше предположение насчёт метода addAll() абсолютно верно. Это стандартный и наиболее часто используемый способ объединения списков в Java. Он добавляет все элементы из указанной коллекции в конец текущего списка. При этом исходные списки остаются нетронутыми, так как addAll() копирует ссылки на объекты (для неизменяемых строк это безопасно).

import java.util.ArrayList;
import java.util.List;

public class MergeListsExample {
    public static void main(String[] args) {
        List<String> listA = new ArrayList<>();
        listA.add("Alice");
        listA.add("Bob");

        List<String> listB = new ArrayList<>();
        listB.add("Charlie");
        listB.add("David");

        // Создаём новый список и добавляем в него элементы из обоих списков
        List<String> mergedList = new ArrayList<>(listA); // Копируем listA
        mergedList.addAll(listB); // Добавляем все элементы из listB

        System.out.println("Объединённый список: " + mergedList);
        // Вывод: [Alice, Bob, Charlie, David]
        
        // Исходные списки не изменились
        System.out.println("listA: " + listA); // [Alice, Bob]
        System.out.println("listB: " + listB); // [Charlie, David]
    }
}

Почему это работает?

  • Конструктор new ArrayList<>(listA) создаёт новый список, содержащий все элементы из listA. Это защищает исходный список от изменений.
  • Метод addAll(listB) добавляет все элементы из listB в конец нового списка. В результате mergedList содержит элементы обоих списков в порядке: сначала из listA, затем из listB.
  • Оба метода работают за O(n) времени, где n — количество добавляемых элементов.

Альтернатива: однострочное решение с помощью Stream API (Java 8+)

Если вы используете Java 8 или новее, можно применить Stream API для более функционального подхода. Этот способ особенно удобен, когда нужно объединить несколько списков или применить дополнительные операции (фильтрацию, сортировку).

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class MergeWithStreams {
    public static void main(String[] args) {
        List<String> listA = List.of("Alice", "Bob");   // Java 9+ удобная фабрика
        List<String> listB = List.of("Charlie", "David");

        List<String> mergedList = Stream.concat(listA.stream(), listB.stream())
                                         .collect(Collectors.toList());

        System.out.println("Объединённый список: " + mergedList);
        // [Alice, Bob, Charlie, David]
    }
}

Что выбрать?

  • addAll() — лучший выбор для простых случаев, когда нужно просто объединить два списка. Код интуитивно понятен и работает во всех версиях Java (начиная с 1.2).
  • Stream API — подходит, если вы уже используете Java 8+ и хотите писать в функциональном стиле, или когда нужно объединить более двух списков (через Stream.of(list1, list2, list3).flatMap(List::stream)).
  • Избегайте ручных циклов — они не только громоздкие, но и менее читаемые. Современная Java предоставляет все необходимые инструменты для лаконичной работы с коллекциями.

Важно помнить: если вы работаете с изменяемыми объектами (например, List<StringBuilder>), то addAll() и Stream API копируют только ссылки. Изменение объекта через ссылку в объединённом списке затронет и исходный список. Для полного копирования используйте глубокое копирование (deep copy), но для строк это неактуально, так как они иммутабельны.

<<>>

Использование ListUtils из Apache Commons Collections

Если в вашем проекте уже используется библиотека Apache Commons Collections, вы можете воспользоваться утилитным классом ListUtils. Это может быть удобно, когда нужно объединить списки без создания временных коллекций вручную.

import org.apache.commons.collections4.ListUtils;
import java.util.ArrayList;
import java.util.List;

public class MergeWithCommons {
    public static void main(String[] args) {
        List<String> listA = new ArrayList<>();
        listA.add("Alice");
        listA.add("Bob");

        List<String> listB = new ArrayList<>();
        listB.add("Charlie");
        listB.add("David");

        // Метод union создаёт новый список, содержащий элементы из обоих списков
        List<String> mergedList = ListUtils.union(listA, listB);
        System.out.println("Объединённый список: " + mergedList);
        // [Alice, Bob, Charlie, David]
    }
}

Важные нюансы

  • Зависимость: Для использования ListUtils необходимо добавить в проект зависимость org.apache.commons:commons-collections4. Это может быть избыточно, если вы используете только этот метод.
  • Поведение: Метод union работает аналогично addAll() — он создаёт новый список и копирует в него элементы. Исходные списки не изменяются.
  • Производительность: Поскольку внутри используется тот же механизм, что и в стандартном addAll(), производительность сопоставима. Однако дополнительная библиотека может увеличить размер приложения.
  • Альтернативы: Если вы не хотите подключать библиотеку, придерживайтесь чистого Java-решения с addAll() или Stream API. Для большинства проектов этого более чем достаточно.

Таким образом, ListUtils.union() — это удобная обёртка, но она оправдана только в том случае, если вы уже используете Apache Commons Collections в проекте. В противном случае лучше остановиться на стандартных средствах Java.