Как работает операция distinct() в Java Stream API?

«Как работает операция distinct() в Java Stream API?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Механизм работы: Для определения уникальности элементов distinct() использует:

  1. Метод equals(Object) для сравнения элементов.
  2. Метод hashCode() для оптимизации (элементы с разным хэш-кодом считаются разными).

Пример:

List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Charlie", "Bob");
List<String> uniqueNames = names.stream()
                                .distinct() // Удаляет дубликаты "Alice" и "Bob"
                                .collect(Collectors.toList());
// Результат: ["Alice", "Bob", "Charlie"] (порядок из исходного списка сохранен)

Критические аспекты для понимания:

  • Для пользовательских объектов: Необходимо корректно переопределять equals() и hashCode(). Без этого distinct() будет сравнивать ссылки на объекты, а не их содержимое.
    // Без переопределения equals/hashCode два разных объекта с одинаковыми полями будут считаться разными.
  • Stateful операция: distinct() должен отслеживать уже увиденные элементы. В параллельных стримах это требует синхронизации и может снижать производительность.
  • Порядок элементов: В упорядоченных стримах (например, созданных из List) порядок уникальных элементов сохраняется (первое вхождение элемента сохраняется, последующие дубликаты отбрасываются). В неупорядоченных стримах порядок не гарантирован.
  • Бесконечные потоки: Использование distinct() на бесконечном потоке может привести к бесконечному выполнению, так как операция ждет завершения потока для гарантии уникальности.