Вступление
Представленный в Java 8, Stream API обычно используется для фильтрации, сопоставления и перебора элементов. При работе с потоками одной из распространенных задач является поиск повторяющихся элементов.
В этом руководстве мы рассмотрим несколько способов поиска повторяющихся элементов в потоке Java.
Collectors.toSet ()
Самый простой способ найти повторяющиеся элементы - это добавить
элементы в Set
. Set
не может содержать повторяющиеся значения, а
метод Set.add()
возвращает boolean
значение, которое является
результатом операции. Если элемент не добавлен, false
, и наоборот.
Давайте создадим Stream
of String
с некоторыми повторяющимися
значениями. Эти значения проверяются с помощью equals()
, поэтому
убедитесь, что он адекватно реализован для пользовательских классов:
Stream<String> stream = Stream.of("john", "doe", "doe", "tom", "john");
Теперь давайте создадим Set
для хранения отфильтрованных элементов. Мы
будем использовать метод filter()
чтобы отфильтровать повторяющиеся
значения и вернуть их:
Set<String> items = new HashSet<>();
stream.filter(n -> !items.add(n))
.collect(Collectors.toSet())
.forEach(System.out::println);
Здесь мы пытаемся add()
каждый элемент в Set
. Если оно не добавлено
из-за дублирования, мы собираем это значение и распечатываем:
john
doe
Collectors.toMap ()
В качестве альтернативы вы также можете подсчитать появление повторяющихся элементов и сохранить эту информацию на карте, которая содержит повторяющиеся элементы в качестве ключей и их частоту в качестве значений.
Создадим List
Integer
типа:
List<Integer> list = Arrays.asList(9, 2, 2, 7, 6, 6, 5, 7);
Затем давайте соберем элементы в Map
и посчитаем их появление:
Map<Integer, Integer> map = list.stream()
.collect(Collectors.toMap(Function.identity(), value -> 1, Integer::sum));
System.out.println(map);
Мы не удаляли никаких элементов, просто подсчитали их появление и
сохранили их в Map
:
{2=2, 5=1, 6=2, 7=2, 9=1}
Collectors.groupingBy (Function.identity (), Collectors.counting ()) с помощью Collectors.toList ()
Метод Collectors.groupingBy()
используется для группировки элементов
на основе некоторого свойства и возврата их как экземпляра Map
В нашем случае метод получает два параметра - Function.identity()
,
который всегда возвращает свои входные аргументы, и
Collectors.counting()
, который считает элементы, переданные в потоке.
Затем мы воспользуемся groupingBy()
чтобы создать карту частотности
этих элементов. После этого мы можем просто filter()
поток для
элементов с частотой выше 1
:
list.stream()
// Creates a map {4:1, 5:2, 7:2, 8:2, 9:1}
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet()
// Convert back to stream to filter
.stream()
.filter(element -> element.getValue() > 1)
// Collect elements to List and print out the values
.collect(Collectors.toList())
.forEach(System.out::println);
Это приводит к:
5=2
7=2
8=2
Если вы хотите извлечь только повторяющиеся элементы без их частоты, вы
можете добавить в процесс map()
После фильтрации и перед сбором в
список мы получим только ключи:
.map(Map.Entry::getKey)
Collections.frequency ()
Collections.frequency()
- еще один метод, который происходит из класса
Java Collections, который подсчитывает вхождения указанного элемента во
входном потоке, просматривая каждый элемент. Он принимает два параметра:
набор и элемент, частота которого должна быть определена.
Теперь мы filter()
поток для каждого элемента, у которого
frequency()
больше 1
:
list.stream()
.filter(i -> Collections.frequency(list, i) > 1)
//Collect elements to a Set and print out the values
.collect(Collectors.toSet())
.forEach(System.out::println);
Здесь мы можем собирать либо в Set
либо в List
. Если мы соберем
список, в нем будут все повторяющиеся элементы, поэтому некоторые могут
повторяться. Если мы соберем в набор, у него будут уникальные
повторяющиеся элементы.
Это приводит к:
5
7
8
Stream.distinct ()
Метод distinct()
- это метод с отслеживанием состояния (сохраняет в
памяти состояние предыдущих элементов) и сравнивает элементы с помощью
метода equals()
Если они уникальны / уникальны, они возвращаются
обратно, и мы можем добавить их в другой список.
Давайте составим список с несколькими повторяющимися значениями и извлечем отдельные значения:
List<String> list = new ArrayList(Arrays.asList("A", "B", "C", "D", "A", "B", "C", "A", "F", "C"));
List<String> distinctElementList = list.stream()
.distinct()
.collect(Collectors.toList());
Теперь все неотличимые значения встречаются более чем один раз. Если мы удалим отдельные значения, у нас останутся повторяющиеся элементы:
for (String distinctElement : distinctElementList) {
list.remove(distinctElement);
}
Теперь распечатаем результаты:
list.forEach(System.out::print)
Это повторяющиеся элементы с соответствующими вхождениями:
ABCAC
Если вы хотите также просмотреть их и показать только одно вхождение
каждого повторяющегося элемента (вместо их всех по отдельности), вы
можете снова distinct()
list.stream()
.distinct()
.collect(Collectors.toList())
.forEach(System.out::print);
Это приводит к:
ABC
Заключение
В этой статье мы рассмотрели несколько подходов к поиску повторяющихся элементов в Java Stream.
Мы рассмотрели метод Stream.distinct()
из Stream API,
Collectors.toSet()
, Collectors.toMap()
и Collectors.groupingBy()
из Java Collectors, а также Collections.frequency()
из Collections.
фреймворк.