Java: поиск повторяющихся элементов в потоке

Введение Представленный в Java 8, Stream API обычно используется для фильтрации, отображения и перебора элементов. При работе с потоками одной из распространенных задач является поиск повторяющихся элементов. В этом руководстве мы рассмотрим несколько способов поиска повторяющихся элементов в потоке Java. Collectors.toSet () Самый простой способ найти повторяющиеся элементы - это добавить элементы в Set. Наборы не могут содержать повторяющиеся значения, а метод Set.add () возвращает логическое значение, которое является

Вступление

Представленный в 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. фреймворк.

comments powered by Disqus