Как использовать здесь метод chaining? Нужно решить это без создания полей, не разрывая цепочки методов
public static void main(String[] args) {
Pattern selectWords = Pattern.compile("[^a-zA-Zа-яА-я0-9]+");
Scanner sc = new Scanner(System.in, "UTF-8").useDelimiter(selectWords);
List<String> buf = new ArrayList<>();
while (sc.hasNext()) {
buf.add(sc.next());
}
Stream<String> words = buf.stream();
Map<String, Long> frqWords = words.map((s) -> s.toLowerCase())
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Stream<Map.Entry<String, Long>> output = frqWords.entrySet().stream();
output.sorted(Comparator.comparing((Map.Entry<String, Long> e) -> e.getKey()))
.sorted(Comparator.comparing((Map.Entry<String, Long> e) ->
e.getValue()).reversed())
.limit(10)
.forEach((Map.Entry<String, Long> e) -> System.out.println(e.getKey()));
}
Ответы (2 шт):
Всё! До меня дошло, как это исправить. Голова просто тяжелая после праздников. Вот так это можно сделать.
public static void main(String[] args) {
Scanner sc = new Scanner(System.in, "UTF-8")
.useDelimiter("[^a-zA-Zа-яА-я0-9]+");
List<String> buf = new ArrayList<>();
while (sc.hasNext()) {
buf.add(sc.next());
}
Основная проблема данного кода в том, что первый же цикл для чтения слов при помощи сканера из потока ввода является бесконечным, то есть, его нужно ограничить хотя бы по общему количеству слов. Если бы чтение выполнялось из файла, такой проблемы не возникло бы.
Также излишне накапливать введенные слова в списке, для которого затем вызывается stream().
Таким образом можно выделить основной функционал по обработке потока слов Stream<String>, чтобы вывести топ N слов по частоте.
Также следует определить, как выводить слова с одинаковой частотой. Если нужно сохранить порядок вхождения во входном потоке, следует использовать LinkedHashMap::new в качестве провайдера мапы. Если нужно отсортировать дополнительно слова по алфавиту, можно использовать TreeMap::new
public static void printTopN(int n, Stream<String> words) {
words.collect(Collectors.groupingBy(
w -> w, LinkedHashMap::new, Collectors.counting()
)) // Map<String, Long>
.entrySet() // Stream<Map.Entry<String, Long>>
.stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(n)
.forEach(System.out::println);
}
Соответственно, такой метод можно вызвать с использованием генератора Stream::generate, с наложением ограничений:
private static Scanner scanner() {
return new Scanner(System.in, "UTF-8")
.useDelimiter("[^a-zA-Zа-яА-я0-9]+");
}
public static void main1(String ... args) {
Scanner scanner = scanner();
printTopN(10, Stream.generate(() -> scanner.next())
.takeWhile(w -> !"quit".equalsIgnoreCase(w) && scanner.hasNext())
.limit(100)
);
}
Или же можно создать поток слов на основании сканера с использованием StreamSupport и Spliterator как показано в ответе на основном SO: How to make scanner strings into a Stream in Java?
private static Stream<String> streamScanner(Scanner scanner) {
return StreamSupport.stream(Spliterators.spliterator(
scanner, Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.NONNULL
), false
);
}
public static void main(String ... args) {
printTopN(10, streamScanner(scanner())
.takeWhile(Predicate.not("quit"::equalsIgnoreCase))
.limit(100)
);
}
Тест:
1 33 2 1 p 1 2 33 44 q w e 1 1 2 33 333 11 22 w e 1 d dd quit
1=6
33=3
2=3
w=2
e=2
p=1
44=1
q=1
333=1
11=1