Метод получения из текста частоты встречаемости слов

Необходимо решить задачу: дан текст, который преобразован в список строк.

Нужно реализовать метод, который возвращает строку, в которой содержатся все слова и количество их упоминаний в тексте в формате котик - 23\n (следовательно, слово котик встречалось в тексте 23 раза). При этом в итоговой строке должны быть слова длиной 4 и больше букв и при этом встречающиеся не реже чем 10 раз. Слова в итоговой строке должны быть отсортированы в порядке убывания их кол-ва упоминаний, а потом уже в алфавитном порядке. Примечание: слова на английском идут после русских.

Я остановился на том, что осталось отсортировать map и преобразовать все в строку:

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Words {
    public String countWords(List<String> lines) {
        String res = "";
        HashMap<String, Integer> map = new HashMap<>();
        for (String s : lines) {
            s = s.replaceAll("[^A-Za-zА-Яа-я]", "").toLowerCase();
            String[] arr = s.split(" ");
            for (String i : arr) {
                if (map.containsKey(i)) {
                    map.put(i, map.get(i) + 1);
                } else {
                    map.put(i, 1);
                }
            }
        }
        // осталось как-то это преобразовать в строку
        return res;
    }
}

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

Автор решения: AlekseiGaile
public String countWords(List<String> lines) {
    StringBuilder res = new StringBuilder();
    HashMap<String, Integer> map = new HashMap<>();
    for (String s : lines) {
        s = s.replaceAll("[^A-Za-zА-Яа-я]", "").toLowerCase();
        String[] arr = s.split(" ");
        for (String i : arr) {
            if (map.containsKey(i)) {
                map.put(i, map.get(i) + 1);
            } else {
                map.put(i, 1);
            }
        }
    }

    for (Map.Entry<String, Integer> entry : map.entrySet()) {
        res.append(entry);
        res.append("\n");
    }
    return res.toString();
}
→ Ссылка
Автор решения: Дмитрий

В целом на быструю руку можно так, но компаратор придется дописать. Сейчас он фильтрует по количеству слов и после этого в алфавитном порядке (латинский алфавит первый). А если хотите русский алфавит первый, то это уже на вашей совести (править надо эту строку: return e1.getKey().compareTo(e2.getKey());).

import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class Main {
    public String countWords(Iterable<String> lines) {
        return findFrequencies(lines).entrySet().stream()
            .filter(e -> e.getValue() >= 10)
            .sorted((e1, e2) -> {
                int compareByFrequencies = e2.getValue().compareTo(e1.getValue());
                if (compareByFrequencies != 0) return compareByFrequencies;
                return e1.getKey().compareTo(e2.getKey());
            })
            .map(e -> e.getKey())
            .collect(Collectors.joining("\r\n"));
    }

    private Map<String, Long> findFrequencies(final Iterable<String> lines) {
        return StreamSupport.stream(lines.spliterator(), false)
            .flatMap(line -> Arrays.stream(line.split(" ")))
            .map(line -> line.replaceAll("[^A-Za-zА-Яа-я]", "").toLowerCase())
            .filter(line -> line != null && line.length() >= 4)
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
    }
}
→ Ссылка
Автор решения: Podushkoved

Подготовка листа со строками:

ArrayList<String> list = new ArrayList<>();

IntStream.range(0, 10).forEach(i -> {
    list.add("Котик пёсик рыбка, птичка? Ёжик..");
    
    list.add("Страна чудес");
    list.add("С нами солнце и луна, шум дождя и тишина..");

    list.add("DON'T WORRY");
    list.add("Here's a little song i wrote..");
});

list.add("Страна чудес");
list.add("DON'T WORRY");

Обход листа:

list.stream()
    // разбиваем строки на слова
    .flatMap(line -> Stream.of(line.split("\\s+")))
    // выкидываем небуквенные символы
    // и приводим к нижнему регистру
    .map(word -> word.replaceAll("[^A-Za-zА-Яа-яЁё]+", "").toLowerCase())
    // слова длиной 4 и больше букв
    .filter(word -> word.length() > 3)
    // собираем в Map<String, Integer>
    // и считаем количество вхождений
    .collect(Collectors.toMap(key -> key, val -> 1, Integer::sum))
    .entrySet().stream()
    // встречающиеся не реже чем 10 раз
    .filter(entry -> entry.getValue() > 9)
    // отсортированы
    .sorted((e1, e2) -> {
        // в порядке убывания их кол-ва упоминаний
        int val = e1.getValue().compareTo(e2.getValue()) * -1;
        if (val == 0) {
            // потом уже в алфавитном порядке
            val = e1.getKey().compareTo(e2.getKey());
            // слова на английском идут после русских
            // тоже в алфавитном порядке
            if (e1.getKey().charAt(0) <= 'z'
                    && e2.getKey().charAt(0) > 'z'
                    || e1.getKey().charAt(0) > 'z'
                    && e2.getKey().charAt(0) <= 'z') {
                val *= -1;
            }
        }
        return val;
    })
    // вывод
    .forEach(e -> System.out.println(e.getKey() + " " + e.getValue()));

Вывод:

страна 11
чудес 11
dont 11
worry 11
дождя 10
котик 10
луна 10
нами 10
птичка 10
пёсик 10
рыбка 10
солнце 10
тишина 10
ёжик 10
heres 10
little 10
song 10
wrote 10
→ Ссылка