Как найти общие элементы и разницу двух списков
Короткий ответ
retainAll() оставляет в коллекции только элементы, которые есть во второй:
void main() {
var a = new ArrayList<>(List.of(1, 2, 3, 4));
var b = List.of(3, 4, 5);
a.retainAll(b);
IO.println(a);
}Результат:
[3, 4]Разница
removeAll() удаляет элементы, которые есть во второй коллекции — остаётся то, чего во второй нет:
void main() {
var all = new ArrayList<>(List.of("Anna", "Oleg", "Boris"));
var done = List.of("Oleg");
all.removeAll(done);
IO.println(all);
}Результат:
[Anna, Boris]Без изменения списков
Поток с HashSet создаёт новый список и не трогает исходные:
void main() {
var a = List.of(1, 2, 3, 4);
var b = new HashSet<>(List.of(3, 4, 5));
IO.println(a.stream().filter(b::contains).toList());
IO.println(a.stream().filter(n -> !b.contains(n)).toList());
}Результат:
[3, 4]
[1, 2]Симметричная разность
Элементы, которые есть только в одном из списков:
void main() {
var a = new HashSet<>(List.of(1, 2, 3));
var b = new HashSet<>(List.of(2, 3, 4));
var onlyA = new TreeSet<>(a);
onlyA.removeAll(b);
var onlyB = new TreeSet<>(b);
onlyB.removeAll(a);
onlyA.addAll(onlyB);
IO.println(onlyA);
}Результат:
[1, 4]Частая ошибка
Передавать большой List в retainAll() или filter(list::contains). Каждая проверка contains() у списка просматривает его целиком. Превратите вторую коллекцию в HashSet — это ускорит код в сотни раз на больших данных.
Функции из этого ответа
- Set.retainAll() — Оставляет в множестве только элементы, которые есть в другой коллекции (пересечение).
- List.remove() — Удаляет элемент списка по индексу или по значению.
- HashSet — Множество без повторов на основе хеш-таблицы; порядок не гарантирован.
- Stream.filter() — Оставляет в потоке только элементы, подходящие под условие.