【问题标题】:Effectively filtering strings in java在java中有效过滤字符串
【发布时间】:2020-03-06 08:35:23
【问题描述】:

我现在正在尝试制作类似迷你搜索引擎的东西。我的目标是在哈希图中索引一堆文件,但首先我需要执行一些操作,包括降低大写字母、删除所有不必要的单词以及删除除 a-z/A-Z 之外的所有字符。现在我的实现看起来像这样:

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);
    }

这只是我代码中的一个块,它可能包含缺失的部分,我用 cmets 粗略地解释了我的算法。使用 .contains 方法检查 stopWords 是否包含任何 currentWord,尽管它是一种更快的方法,但它不会映射像“death”这样的单词,因为它包含停用词列表中的“at”。 我尽我所能让它更有效,但我没有太大进展。每个文件包含大约。考虑到我有一万个文件,每个约 300 个字需要约 3 秒来索引,这并不理想。关于如何改进我的算法以使其运行更快的任何想法?

【问题讨论】:

  • 您正在读取每个源文件的停用词文件。您可以读取一次停用词文件并使用Set 将停用词存储在内存中。

标签: java string eclipse indexing hashmap


【解决方案1】:

有一些改进:

首先请不要使用 new Scanner(File) 构造函数,因为它使用无缓冲 I/O。小型磁盘读取操作,尤其是在 HDD 上的操作非常无效。例如,使用 65 KB 缓冲区的 BufferedInputStream:

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

第二:您的 PC 很可能有一个多代码 CPU。因此,您可以并行扫描多个文件。 要做到这一点,您必须确保使用多线程感知 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();
    }
});

根据系统中的 CPU 内核,它会同时处理多个文件。特别是如果您处理大量文件,这将大大减少您的程序的运行时间。

最后你的实现相当复杂。您使用 Scanner 的输出创建一个新的字符串,然后再次拆分。相反,最好将 Scanner 配置为直接考虑您想要的分隔符:

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

然后你可以直接使用 Scanner 创建的令牌,而不必构建 article 字符串然后拆分它。

【讨论】:

    【解决方案2】:

    自己实现搜索引擎的原因是什么?

    对于生产,我会推荐现有的解决方案 - Apache Lucene,它完全符合您的任务。

    如果您只是在训练,有几个标准点可以改进您的代码。

    1. 避免像article += 这样的循环中的字符串连接。最好创建一个单词正则表达式并将其传递给 Scanner。
        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);
                ...
            }
        }
    
    1. 将所有停用词放入 hashmap 并使用 containsKey 方法检查每个新词

    【讨论】:

      猜你喜欢
      • 2013-04-13
      • 2018-09-18
      • 2018-06-17
      • 1970-01-01
      • 2020-05-29
      • 2018-05-05
      • 2016-07-16
      • 1970-01-01
      • 2011-05-17
      相关资源
      最近更新 更多