При подсчете слов в строке появляется {}

Делаю программу для подсчёта повторения каждого слово из файла что приходит в метод. Я хочу чтоб на выходе возвращался String вот в таком виде пушкин - 20, для каждого слово. А мне возвращается пустые скобки {}. Как решить проблему?

public String countWords(List<String> lines) {
            Map<String, Integer> wordCount = new HashMap<String, Integer>();
            String learn = lines.toString().toLowerCase().replaceAll("[^a-zA-Z0-9]", " ");
            String[] words = learn.split(" ");
    
            for(String word: words) {
                Integer count = wordCount.get(word);
                wordCount.put(word, (count==null) ? 1 : count+1);
            }
            return wordCount.toString();
        }
    }

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

Автор решения: Alex Rudenko

Проблема в том, что .replaceAll("[^a-zA-Z0-9]", " ") заменяет все символы кроме цифр и английских/латинских букв на пробелы. Следовательно, все слова на русском удаляются, а результирующая мапа получается пустой, о чём и говорит результат в виде пары фигурных скобок {}.

Для фильтрации букв можно использовать POSIX-классы \p{L} для букв и \p{N} для цифр: replaceAll("(?u)[^\\p{L}\\p{N}]", " ").

Также стоит отфильтровать пустые строки - либо уже в цикле, либо используя разбиение по группам пробельных символов \s+:

String[] words = learn.trim().split("\\s+");

Также, для подсчёта частоты можно использовать более лаконичный метод Map::merge:

public static String countWords(List<String> lines) {
    Map<String, Integer> wordCount = new HashMap<String, Integer>();
    
    for (String word : lines.toString().toLowerCase()
        .replaceAll("(?u)[^\\p{L}\\p{N}]", " ")
        .trim().split("\\s+")
    ) {
        wordCount.merge(word, 1, Integer::sum);
    }
    return wordCount.toString();
}

Тест:

System.out.println(countWords(Arrays.asList("Пушкин пушкин -- ПУШКИН", "test test2 test")));
// -> {test2=1, test=2, пушкин=3}

Следует отметить, что эта задачу удобно решить, используя Stream API, в частности, Collectors.groupingBy и Collectors.counting для подсчета частоты слов, а затем Collectors.joining для преобразования в строку:

public static String countWords(List<String> lines) {
    return lines
        .stream() // поток строк
        .map(line -> line.toLowerCase()
            .replaceAll("(?u)[^\\p{L}\\p{N}]", " ")
        ) // поток строк в нижнем регистре без ненужных символов
        .flatMap(line -> Arrays.stream(line.split("\\s+"))) // поток слов
        .collect(Collectors.groupingBy(
            word -> word,
            TreeMap::new, // сортировка по ключу по умолчанию
            Collectors.counting()
        )) // HashMap<String, Long> - словарь со значениями частот
        .entrySet()
        .stream()
        .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
        .map(e -> e.getKey() + " - " + e.getValue())
        .collect(Collectors.joining("\n")); // результат
}    

Тест:

System.out.println(countWords(Arrays.asList("Пушкин пушкин -- ПУШКИН наше", "тигруля красив котенок игрив", "красив манул игрив")));

Результат:

пушкин - 3
игрив - 2
красив - 2
котенок - 1
манул - 1
наше - 1
тигруля - 1
→ Ссылка