#java
#java
Вопрос:
Допустим, у меня есть List<Set<String>>
, который выглядит следующим образом:
[A,B,C,D]
[B,C,D,E]
[C,D,E,F]
[D,E,F,G]
Если я хочу использовать каждое значение (A, B, C, D, E, F, G) в каждом Set<String>
из них List<Set<String>>
и подсчитывать их появление, сопоставляя их, какой хороший способ для меня это реализовать? Я хочу, чтобы результат выглядел примерно так:
A: 1
B: 2
C: 3
D: 4
E: 3
F: 2
G: 1
Ответ №1:
Просто сведите список в один поток и используйте groupingBy
коллектор.
- сначала просто передайте список. Это создает поток меньших списков.
- затем вам нужно передать их в потоковом режиме. Но вам не нужны 4 потока писем. Вам нужен один поток из 16 букв (или сумма всех наборов). Это то, что делает flatMap. Он объединяет несколько потоков в один.
- затем вы хотите выполнить подсчет частоты. Итак, вы хотите сгруппировать буквы, используя их в качестве ключа. По умолчанию
groupingBy
будет создан список и помещены коллизии (значения, связанные с дубликатами ключей) в список. - но вы этого не хотите, поэтому
Collectors.counting()
говорит, что если вы видите другой ключ, который уже есть, просто подсчитайте и обновите значение на 1. Итак, вы подсчитываете вхождения ключей.
List<Set<String>> list = List.of(Set.of("A", "B", "C", "D"),
Set.of("B", "C", "D", "E"),
Set.of("C", "D", "E", "F"),
Set.of("D", "E", "F", "G"));
Map<String, Long> freq =
list.stream().flatMap(Set::stream).collect(Collectors
.groupingBy(a -> a, Collectors.counting()));
freq.entrySet().forEach(System.out::println);
С принтами
A=1
B=2
C=3
D=4
E=3
F=2
G=1
Вот простой пример поведения по умолчанию groupingBy
. Он просто помещает значения в список на основе их остатков при делении на 10
. IntStream
генерирует поток int primitives
, поэтому их необходимо преобразовать в объект ( Integer
в данном случае) для сбора.
Map<Integer, List<Integer>> remainders =
IntStream.range(0, 100).mapToObj(Integer::valueOf)
.collect(Collectors.groupingBy(n -> n % 10));
remainders.entrySet().forEach(System.out::println);
С принтами
0=[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
1=[1, 11, 21, 31, 41, 51, 61, 71, 81, 91]
2=[2, 12, 22, 32, 42, 52, 62, 72, 82, 92]
3=[3, 13, 23, 33, 43, 53, 63, 73, 83, 93]
4=[4, 14, 24, 34, 44, 54, 64, 74, 84, 94]
5=[5, 15, 25, 35, 45, 55, 65, 75, 85, 95]
6=[6, 16, 26, 36, 46, 56, 66, 76, 86, 96]
7=[7, 17, 27, 37, 47, 57, 67, 77, 87, 97]
8=[8, 18, 28, 38, 48, 58, 68, 78, 88, 98]
9=[9, 19, 29, 39, 49, 59, 69, 79, 89, 99]
Комментарии:
1. Работает отлично, но не могли бы вы помочь мне немного подробнее рассказать об алгоритме, который вы реализовали в части .collect() ?
Ответ №2:
List<Set<String>> input = new ArrayList<>();
input.add(Set.of("A", "B", "C", "D"));
input.add(Set.of("B", "C", "D", "E"));
input.add(Set.of("C", "D", "E", "F"));
input.add(Set.of("D", "E", "F", "G"));
input.stream()
.flatMap(Collection::stream)
.collect(groupingBy(Function.identity(), counting()))
.entrySet()
.forEach(System.out::println);
импорт
import java.util.*;
import java.util.function.Function;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
Ответ №3:
Используя Stream API, flatMap
для получения потока строк следует использовать внутренние наборы, после чего создается частотная карта:
List<Set<String>> data = Arrays.asList(
Set.of("A", "B", "C", "D"),
Set.of("B", "C", "D", "E"),
Set.of("C", "D", "E", "F"),
Set.of("D", "E", "F", "G")
);
data.stream()
.flatMap(Set::stream)
.collect(Collectors.toMap(s -> s, s -> 1, Integer::sum, LinkedHashMap::new))
.entrySet()
.forEach(System.out::println);
Вывод:
A=1
B=2
C=3
D=4
E=3
F=2
G=1