Эффективная фильтрация строк в Java - PullRequest
0 голосов
/ 10 ноября 2019

Я пытаюсь сделать что-то вроде мини-поисковика прямо сейчас. Моя цель состоит в том, чтобы проиндексировать несколько файлов в хэш-карте, но сначала мне нужно выполнить пару операций, которые включают в себя снижение прописных букв, удаление всех ненужных слов, а также удаление всех символов, кроме az / AZ. Прямо сейчас моя реализация выглядит следующим образом:

String article = "";

for (File file : dir.listFiles()) { //for each file (001.txt, 002.txt...)
        Scanner s = null;
        try {
            s = new Scanner(file);
            while (s.hasNext())
                article += s.next().toLowerCase(Locale.ROOT) + " "; //converting all characters to lower case
            article = currentWord.replaceAll(delimiters.get()," "); //removing punctuations (?, -, !, * etc...) 

            String splittedWords = article.split(" ");  //splitting each word into a string array
            for(int i = 0; i < splittedWords.length; i++) {
                s = new Scanner(stopwords);
                boolean flag = true;
                while(s.hasNextLine())
                    if (splittedWords[i].equals(s.nextLine())) { //comparing each word with all the stop words (words like a, the, already, these etc...) taken from another big txt file and removing them, because we dont need to fill our map with unnecessary words, to provide faster search times later on
                        flag = false;
                        break;
                    }
                if(flag) map.put(splittedWords[i], file.getName()); //if current word in splittedWords array does not match any stop word, put it in the hashmap        


            }
            s.close();


        } catch (FileNotFoundException e) {

            e.printStackTrace();
        }
        s.close();
        System.out.println(file);
    }

это всего лишь блок из моего кода, он может содержать недостающие фрагменты, я объяснил свой алгоритм кратко с комментариями. Использование метода .contains для проверки того, содержит ли stopWords какой-либо currentWord, хотя он и более быстрый, он не отображает такие слова, как «смерть», поскольку содержит «at» из списка стоп-слов. Я пытаюсь сделать все возможное, чтобы сделать его более эффективным, но я не очень прогрессировал. каждый файл содержит ок. ~ 300 слов каждое занимает ~ 3 секунды для индексации, что не идеально, учитывая, что у меня есть десять тысяч файлов. Любые идеи о том, как я могу улучшить свой алгоритм, чтобы он работал быстрее?

Ответы [ 2 ]

1 голос
/ 10 ноября 2019

Есть некоторые улучшения:

Во-первых, пожалуйста, не используйте конструктор new Scanner(File), так как он использует небуферизованный ввод / вывод. Операции чтения небольших дисков, особенно на жестких дисках, очень неэффективны. Вместо этого используйте, например, BufferedInputStream с буфером 65 КБ:

try (Scanner s = new Scanner(new BufferedInputStream(new FileInputStream(f), 65536))) {
    // your code
}

Секунда: Скорее всего, ваш ПК имеет многокодовый процессор. Поэтому вы можете сканировать несколько файлов параллельно. Для этого вы должны убедиться, что вы используете многопоточную map. Измените определение карты на:

Map<String,String> map = new ConcurrentHashMap<>();

Затем вы можете использовать следующий код:

Files.list(dir.toPath()).parallel().forEach(f -> {
    try (Scanner s = new Scanner(new BufferedInputStream(Files.newInputStream(f), 65536))) {
        // your code
    } catch (IOException e) {
        e.printStackTrace();
    }
});

В зависимости от ядер ЦП в вашей системе он будет обрабатывать несколько файлов одновременновремя. Особенно, если вы обрабатываете большое количество файлов, это значительно сократит время выполнения вашей программы.

Наконец ваша реализация довольно сложна. Вы используете вывод Scanner для создания новой строки, которая затем снова разделяется. Вместо этого было бы лучше настроить Scanner так, чтобы он непосредственно рассматривал нужный разделитель:

try (Scanner s = new Scanner(....).useDelimiter("[ ,\\!\\-\\.\\?\\*]")) {

Тогда вы можете напрямую использовать токены, созданные Scanner, и вам не нужно создавать строку article String и более позднюю разбивку. Это.

0 голосов
/ 10 ноября 2019

в чем причина внедрения поисковой системы самостоятельно?

Для производства я бы порекомендовал существующее решение - Apache Lucene, которое идеально соответствует вашей задаче.

Если вы только тренируетесь, есть несколькостандартные точки для улучшения вашего кода.

  1. Избегайте конкатенации строк в цикле, как этот article +=. Лучше создать слово regexp и передать его в сканер.
    Pattern p = Pattern.compile("[A-Za-z]+");
    try (Scanner s = new Scanner(file)) {
        while (s.hasNext(p)) {
            String word = s.next(p);
            word = word.toLowerCase(Locale.ROOT);
            ...
        }
    }
Поместите все стоп-слова в hashmap и проверьте каждое новое слово только с помощью containsKey метода
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...