在 Java 中使用 hashmap 进行单词列表搜索

Word list search with hashmap in Java

我有一个单词列表,我的单词列表中有超过 50,000 个单词。如您所见,我读了我的话并将它们添加到一个数组列表中,但是在这个过程之后,当我想读我的话时,它发生得非常慢。这就是我想到 Hashmap 的原因。我想阅读我的文字,当我收到用户输入的文字时,我想检查它是否在 HashMap 中。即使我做了研究,我也找不到确切的方法。我该怎么做?

  public ArrayList<String> wordReader () throws FileNotFoundException {
        File txt = new File(path);
        Scanner scanner = new Scanner(txt);
        ArrayList <String> words = new ArrayList<String>();
        while (scanner.hasNextLine()) {
            String data = scanner.nextLine();
            words.add(data);
        }
        scanner.close();
        return words;
    }

我会使用 Set,而不是 List,因为当您将重复项添加到集合中时,集合会自动忽略重复项。如果它不存在,它 returns 为真并添加它,否则为假。

public Set<String> wordReader () throws FileNotFoundException {
        File txt = new File(path);
        Scanner scanner = new Scanner(txt);
        Set <String> words = new HashSet<>();
        while (scanner.hasNextLine()) {
            String data = scanner.nextLine();
            if(!words.add(data)) {
               // present - Do something
            } 
         }   
        
        scanner.close();
        return words;
}
  • 因为集合没有排序,所以它们不是随机访问集合。因此,您可以将集合添加到列表中,如下所示:
Set<String> words = wordReader();
List<String> wordList = new ArrayList<>(words);

现在您可以使用索引检索它们。

  • 您可能希望通过将文件名作为参数传递来使您的方法更加通用。

由于您将检查输入的单词是否出现在从文件读取的单词列表中,因此您可以使用 HashSet<String> 而不是 ArrayList<String>

您的方法将变成

public HashSet<String> wordReader () throws FileNotFoundException {
        File txt = new File(path);
        Scanner scanner = new Scanner(txt);
        HashSet <String> words = new HashSet<String>();
        while (scanner.hasNextLine()) {
            String data = scanner.nextLine();
            words.add(data);
        }
        scanner.close();
        return words;
    }

现在,在您阅读输入的单词后,您可以检查它是否出现在 HashSet 中。这将是一个更快的操作,因为查找将花费恒定的时间。

public boolean isWordPresent(String word, HashMap<String> words){
    return words.contains(word);
}

附带说明一下,HashSet 在内部使用 HashMap 来执行操作。

如果我没有正确理解你的问题,当你试图检查列表中是否存在特定单词时,你在遍历充满 50.000 个单词的 ArrayList 时遇到了性能问题。

这是因为在未排序的 List 中查找元素具有 O(n) 的复杂性。您可以通过使用像 BST(二叉搜索树)这样的排序数据结构来提高性能,这将改进具有 O(log n) 复杂度的研究操作。

此外,您使用 Map 的想法绝对可行,因为 HashMap 允许在 O(1)[=43= 之间添加和获取操作的复杂性](对于理论上完美的哈希算法,密钥之间根本没有冲突)和 O(n)(对于碰撞可能性很高的糟糕哈希算法)。另外,从Java 8开始,在HashMap的实现中引入了一个优化,在多个元素加入同一个桶的高碰撞条件下,一个桶对应的数据结构实际上实现为:平衡树而不是列表,在最坏的情况下授予 O(log n) 复杂性。

https://www.logicbig.com/tutorials/core-java-tutorial/java-collections/java-map-cheatsheet.html

但是,使用 HashMap 作为我假设的字典(只有不同​​的词)可能是不必要的,因为您会使用一个词作为键和值。正如其他人指出的那样,您可以使用 Set,或者更好的 HashSet,而不是 HashMap。事实上,HashSet 是通过引擎盖下的 HashMap 实例实现的,这将为我们提供前面讨论的所有性能和优势(这就是我写那篇序言的原因)。

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/HashSet.html

您的实现可能如下所示:

public Set<String> wordReader(String path) throws FileNotFoundException {
    File txt = new File(path);
    Scanner scanner = new Scanner(txt);
    Set<String> words = new HashSet<>();
    while (scanner.hasNextLine()) {
        String data = scanner.nextLine();
        words.add(data);
    }
    scanner.close();
    return words;
}

public boolean isWordContained(Set<String> set, String word) {
    return set.contains(word);
}