diff --git a/src/main/java/ru/naumen/collection/task3/WarAndPeace.java b/src/main/java/ru/naumen/collection/task3/WarAndPeace.java index 906b23a..27cce77 100644 --- a/src/main/java/ru/naumen/collection/task3/WarAndPeace.java +++ b/src/main/java/ru/naumen/collection/task3/WarAndPeace.java @@ -1,6 +1,11 @@ package ru.naumen.collection.task3; +import javax.swing.*; +import java.awt.*; import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.PriorityQueue; /** *

Написать консольное приложение, которое принимает на вход произвольный текстовый файл в формате txt. @@ -14,15 +19,126 @@ */ public class WarAndPeace { - private static final Path WAR_AND_PEACE_FILE_PATH = Path.of("src/main/resources", "Лев_Толстой_Война_и_мир_Том_1,_2,_3,_4_(UTF-8).txt"); + /** + * Хеш-таблица для хранения уникальных слов и их частоты встречаемости + */ + private static final LinkedHashMap wordCount = new LinkedHashMap<>(); + + /** + * Мини-куча для хранения 10 наименее используемых слов (по значению счетчика) + * и Макси-куча для хранения 10 наиболее используемых слов (по значению счетчика) + *

+ * В PriorityQueue первый элемент - наименьший (минимальный) по заданному компаратору. + * Поэтому для low10 (наименее используемых) используется компаратор, который упорядочивает + * элементы по убыванию (от большего к меньшему), чтобы на вершине очереди был элемент с наибольшим значением. + * Для high10 (наиболее используемых) используется естественный порядок (по возрастанию), + * чтобы на вершине очереди был элемент с наименьшим значением. + *

+ * Таким образом, когда размер очереди превышает 10, удаляется элемент с наибольшим значением + * для low10 и с наименьшим значением для high10, что позволяет эффективно + * поддерживать только 10 наименее и наиболее используемых слов соответственно. + */ + private static final PriorityQueue> low10 = + new PriorityQueue<>( + (a, b) -> + b.getValue().compareTo(a.getValue()) + ); + + private static final PriorityQueue> high10 = + new PriorityQueue<>( + Map.Entry.comparingByValue() + ); + + /** + * Точка входа в программу + *
+ *

Алгоритм: + *

+ * @param args аргументы командной строки (не используются) + */ public static void main(String[] args) { new WordParser(WAR_AND_PEACE_FILE_PATH) - .forEachWord(word -> { - // TODO ваше действие над word - }); - // TODO выполнить задачу + .forEachWord(word -> wordCount.merge(word, 1, Integer::sum)); + + counter(); + showResults(high10, low10); + } + + /** + * Заполняет две кучи (минимальную и максимальную) на основе данных из хеш-таблицы + *

+ * Алгоритм: + *

+ * Таким образом, в каждой куче будут храниться только 10 наиболее и наименее используемых слов соответственно. + *

+ * Сложность алгоритма: O(n log k), где n - количество уникальных слов, k - размер кучи (10 в данном случае). + * Выбор алгоритма обоснован практической эффективностью: + * использование куч позволяет эффективно поддерживать топ-k элементов + * без необходимости сортировки всех элементов, что особенно важно при большом количестве уникальных слов. + */ + private static void counter() { + for (Map.Entry entry : WarAndPeace.wordCount.entrySet()) { + WarAndPeace.high10.offer(entry); + WarAndPeace.low10.offer(entry); + + if (WarAndPeace.high10.size() > 10) WarAndPeace.high10.poll(); + if (WarAndPeace.low10.size() > 10) WarAndPeace.low10.poll(); + } + } + + /** + * Отображает результаты в графическом окне + *

+ * Алгоритм: + *

+ *

+ * @param high10 минимальная куча для хранения 10 наиболее используемых слов + * @param low10 максимальная куча для хранения 10 наименее используемых слов + */ + public static void showResults(PriorityQueue> high10, + PriorityQueue> low10) { + JFrame frame = new JFrame("Статистика слов"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setMinimumSize(new Dimension(400, 600)); + + JTextArea textArea = new JTextArea(20, 35); + textArea.setEditable(false); + textArea.setFont(new Font("SansSerif", Font.PLAIN, 16)); + + StringBuilder sb = new StringBuilder(); + sb.append("TOP 10 наиболее используемых слов:\n\n"); + high10.stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .forEach(e -> sb.append(e + .getKey()).append(" - ").append(e.getValue()).append(" раз(а)\n")); + + sb.append("\n10 наименее используемых:\n\n"); + low10.stream() + .sorted(Map.Entry.comparingByValue()) + .forEach(e -> sb.append(e + .getKey()).append(" - ").append(e.getValue()).append(" раз(а)\n")); + + textArea.setText(sb.toString()); + frame.add(new JScrollPane(textArea)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); } }