Введение: зачем объединять списки в Java и как не запутаться
Представьте: у вас есть два списка задач — «срочные» и «важные». Или два списка клиентов — из базы за прошлый месяц и за текущий. Чтобы работать с ними как с единым целым, их нужно объединить. В Java эта задача встречается настолько часто, что вопрос «How do I join two lists in Java?» на Stack Overflow собирает тысячи просмотров. И это не случайно: у новичков объединение списков часто вызывает путаницу — то ошибка компиляции, то список оказывается неизменяемым, то теряются данные.
В этой статье мы разберём 5 проверенных способов объединить два списка в Java — от классического addAll до элегантного Stream API. Вы узнаете, какой метод выбрать для разных ситуаций, как избежать подводных камней и написать код, который будет работать быстро и предсказуемо. Поехали!
Способ 1: Классический addAll — просто и надёжно
Самый прямой путь — использовать метод addAll() из интерфейса Collection. Он добавляет все элементы одной коллекции в другую. Звучит идеально, но есть нюанс: он изменяет исходный список. Если вам нужно сохранить оригинальные списки нетронутыми — создавайте новый.
Пример: объединение с сохранением оригиналов
import java.util.ArrayList;
import java.util.List;
public class JoinListsExample {
public static void main(String[] args) {
List<String> listA = new ArrayList<>(List.of("Java", "Python", "C++"));
List<String> listB = new ArrayList<>(List.of("JavaScript", "Go", "Rust"));
// Создаём новый список и добавляем в него оба списка
List<String> mergedList = new ArrayList<>(listA);
mergedList.addAll(listB);
System.out.println(mergedList); // [Java, Python, C++, JavaScript, Go, Rust]
// Оригинальные списки не изменились
}
}
Плюсы: интуитивно понятно, работает с любыми типами List, не требует Java 8+.
Минусы: изменяет исходный список, если не создать новый; не поддерживает ленивые вычисления.
Способ 2: Stream API — современный и гибкий
Если вы используете Java 8 или новее, Stream API предлагает элегантный способ объединения списков. Он не изменяет исходные данные и легко комбинируется с другими операциями — фильтрацией, сортировкой, трансформацией.
Базовое объединение через Stream.concat
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamJoinExample {
public static void main(String[] args) {
List<String> listA = List.of("Красный", "Зелёный", "Синий");
List<String> listB = List.of("Жёлтый", "Фиолетовый");
List<String> mergedList = Stream.concat(listA.stream(), listB.stream())
.collect(Collectors.toList());
System.out.println(mergedList); // [Красный, Зелёный, Синий, Жёлтый, Фиолетовый]
}
}
Объединение с фильтрацией и сортировкой
Допустим, нужно объединить два списка чисел, оставить только чётные и отсортировать по убыванию:
import java.util.List;
import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamAdvancedJoin {
public static void main(String[] args) {
List<Integer> listA = List.of(5, 2, 8, 1);
List<Integer> listB = List.of(7, 4, 9, 3);
List<Integer> result = Stream.concat(listA.stream(), listB.stream())
.filter(n -> n % 2 == 0) // только чётные
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println(result); // [8, 4, 2]
}
}
Плюсы: функциональный стиль, ленивые вычисления, легко комбинировать с другими операциями.
Минусы: требует Java 8+, немного сложнее для новичков.
Способ 3: ListUtils из Apache Commons Collections
Если вы уже используете библиотеку Apache Commons Collections (а это популярное решение в enterprise-проектах), утилита ListUtils упрощает объединение до одной строки.
Для начала добавьте зависимость в pom.xml (Maven):
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
Пример использования
import org.apache.commons.collections4.ListUtils;
import java.util.List;
public class ApacheJoinExample {
public static void main(String[] args) {
List<String> listA = List.of("A", "B", "C");
List<String> listB = List.of("D", "E");
List<String> merged = ListUtils.union(listA, listB);
System.out.println(merged); // [A, B, C, D, E]
}
}
Важно: ListUtils.union возвращает новый список и не изменяет исходные.
Плюсы: краткий код, не нужно писать утилиты самому.
Минусы: добавляет зависимость в проект; метод union работает как addAll — не удаляет дубликаты.
Способ 4: Plain Java — создаём свой утилитный метод
Иногда хочется иметь под рукой собственный метод, который делает ровно то, что нужно. Например, объединять списки с удалением дубликатов или с проверкой на null. Напишем универсальный метод:
import java.util.ArrayList;
import java.util.List;
public class ListUtilsCustom {
// Объединяет два списка, игнорируя null
public static <T> List<T> safeJoin(List<T> list1, List<T> list2) {
List<T> result = new ArrayList<>();
if (list1 != null) result.addAll(list1);
if (list2 != null) result.addAll(list2);
return result;
}
// Объединяет два списка, удаляя дубликаты (порядок первого списка сохраняется)
public static <T> List<T> joinUnique(List<T> list1, List<T> list2) {
List<T> result = new ArrayList<>(list1);
for (T item : list2) {
if (!result.contains(item)) {
result.add(item);
}
}
return result;
}
public static void main(String[] args) {
List<String> a = List.of("один", "два", "три");
List<String> b = List.of("два", "четыре");
System.out.println(safeJoin(a, b)); // [один, два, три, два, четыре]
System.out.println(joinUnique(a, b)); // [один, два, три, четыре