#java #algorithm #file #word #counting
#java #алгоритм #файл #word #подсчет
Вопрос:
Мне нужно разработать приложение, которое извлекает наибольшее количество вхождений слов, присутствующих в 3 файлах txt, обратите внимание, что все слова сортируются по возрастанию внутри этих файлов, а некоторые слова могут присутствовать в нескольких файлах. Для запуска приложения мы будем использовать команду -Xmx32m (которая делает максимальный размер кучи 32 МБ) в качестве примера ниже:
java -Xmx32m наибольшее количество встречаемости слов в 3 файлах
Я выполнил приложение, используя hashmap и связанные списки, но как только я пытаюсь использовать команду -Xmx32m, приложение выдает ошибку нехватки памяти. Если кто-нибудь может помочь с какими-либо идеями, я буду благодарен.
Вы можете найти код ниже;
package topkwordsocurrences;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.*;
public class TopKWordOcurrences {
public static void getKWordOcurrences() {
Scanner firstReader, secondReader, thirdReader;
Map<String, Integer> uniqueWords = new LinkedHashMap<>();
String line;
try {
PrintWriter out = new PrintWriter(".\src\topkwordsocurrences\out.txt");
firstReader = new Scanner(new FileReader(".\src\topkwordsocurrences\f1.txt"));
secondReader = new Scanner(new FileReader(".\src\topkwordsocurrences\f2.txt"));
thirdReader = new Scanner(new FileReader(".\src\topkwordsocurrences\f3.txt"));
while (firstReader.hasNextLine()) {
line = firstReader.nextLine();
if (uniqueWords.containsKey(line)) {
// if your keys is already in map, increment count of it
uniqueWords.put(line, uniqueWords.getOrDefault(line, 0) 1);
} else {
// if it isn't in it, add it
uniqueWords.put(line, 1);
}
}
while (secondReader.hasNextLine()) {
line = secondReader.nextLine();
if (uniqueWords.containsKey(line)) {
// if your keys is already in map, increment count of it
uniqueWords.put(line, uniqueWords.getOrDefault(line, 0) 1);
} else {
// if it isn't in it, add it
uniqueWords.put(line, 1);
}
}
while (thirdReader.hasNextLine()) {
line = thirdReader.nextLine();
if (uniqueWords.containsKey(line)) {
// if your keys is already in map, increment count of it
uniqueWords.put(line, uniqueWords.getOrDefault(line, 0) 1);
} else {
// if it isn't in it, add it
uniqueWords.put(line, 1);
}
}
//convert HashMap into List
List<Map.Entry<String, Integer>> list = new ArrayList<>(uniqueWords.entrySet());
//sorting the list elements in descending order // to make ascending order alter return o1.getValue().compareTo(o2.getValue());
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
List<Map.Entry<String, Integer>> top5 = list.subList(0, 5);
for (Map.Entry<String, Integer> i : top5) {
System.out.println(i.getKey());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
getKWordOcurrences();
}
}
Ответ №1:
Есть несколько вещей, которые вы можете сделать, чтобы уменьшить потребление памяти вашей программой.
Во-первых, вы можете закрыть каждый файл после его чтения. В настоящее время вы открываете 3 устройства чтения и не закрываете их, пока программа не завершится. Вы можете добиться большего:
String[] fileNames = new String[]{
".\src\topkwordsocurrences\f1.txt",
".\src\topkwordsocurrences\f2.txt",
".\src\topkwordsocurrences\f3.txt"
};
for (String fileName : fileNames) {
try (Scanner s = new Scanner(new FileReader(fileName))) {
while (s.hasNextLine()) {
processWord(s.nextLine());
}
} // file is closed when this line is reached, even if exceptions thrown
}
Обратите внимание, что приведенный выше подход также устраняет много дублирования: вам нужно только реализовать processWord
или логику для открытия файлов один раз.
Вы используете a LinkedHashmap
для хранения слов. Сколько уникальных слов в ваших файлах? Если это обычный текст, у вас не должно возникнуть проблем с сохранением минимального объема памяти. Однако, если слова генерируются машиной, ни один выбор карты не сможет сохранить их все в памяти. Между ними другие типы реализации map могут требовать меньше памяти — например, a HashMap
, не сохраняя порядок, требует несколько меньше места на запись, чем a LinkedHashmap
. Использование a Scanner
также сопряжено с накладными расходами (и оно выполняет опережающее чтение для повышения производительности при небольших затратах памяти). Если вам не хватает памяти, может сработать прямое использование считывателя.
Если у вас много разных слов и, несмотря на внесение этих изменений, вам не хватает памяти, вы можете вообще не использовать хэш-карту: вы ищете только самое частое слово или K наиболее часто встречающихся слов, поэтому должно быть достаточно сохранить только верхние K слов и их частоты.
В псевдокоде:
open all scanners
for each scanner,
count all occurrences of its 1st word
until all scanners are empty:
sort last-read-words from scanners
merge counts for the lexicographically smallest word (if found in several scanners)
check to see if smallest word should enter into top-k list
for each scanner which had smallest word,
count occurrences for its next word