Изменения кода при помощи lambda и Streams

Сделал программу которая считает сколько раз каждое слово повторяется в файле и теперь пытаюсь его сделать при помощи lambda и Streams, но не очень получается.

Map<String, Integer> wordCount = new HashMap<String, Integer>();

        String[] words = learn.split(" ");

        for (int i=0 ; i < words.length ; i++){
            if (!wordCount.containsKey(words[i])){
                wordCount.put(words[i],1);
            } else{
                wordCount.put(words[i],wordCount.get(words[i])+1);
            }
        }
        Set<Map.Entry<String, Integer>> setOfEntries = wordCount.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = setOfEntries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            Integer value = entry.getValue();
            String key = entry.getKey();
            if ((value.compareTo(Integer.valueOf(10)) <0) || key.length() <4)  {

                iterator.remove();
            }
        }
        List<Map.Entry<String, Integer>> list = new ArrayList<>();
        list.addAll(wordCount.entrySet());

Как можно этот кусок кода изменить при помощи lambda и Streams


Ответы (1 шт):

Автор решения: Alex Rudenko
  • При помощи Files.lines получить поток строк в файле Stream<String>
  • Каждую строку разбить на слова и использовать flatMap для преобразования в поток слов
  • при необходимости отфильтровать слова по длине
  • Использовать Collectors.groupingBy + Collectors.counting для получения карты частоты слов Map<String, Long>
Map<String, Long> wordFreq = Files.lines(Paths.get("input.txt")) // Stream<String>
    .flatMap(line -> Arrays.stream(line.split("\\s+")))
    .filter(word -> word.length() > 2) // игнорировать слова короче 3 символов
    .collect(Collectors.groupingBy(
        word -> word,
        Collectors.counting()
    ));

Для получения частоты в виде Integer можно применить коллектор Collectors.summingInt:

// ...
    .collect(Collectors.groupingBy(
        word -> word,
        Collectors.summingInt(word -> 1) // каждому слову отвечает единица
    )); // Map<String, Integer>

Также можно привести слова к одинаковому регистру word -> word.toLowerCase(), разбивать не только по пробелам, но и знакам препинания line -> line.split("[\\p{Punct}\\s]+"), использовать SortedMap/TreeMap с сортировкой слов из коробки или LinkedHashMap для вывода слов в порядке их появления во входном файле, и т.д.

→ Ссылка