Java 8 Streams - 如何比较元素?

dzi*_*zak 2 java inputstream fileinputstream anagram java-stream

我想.txt使用 Java Stream在文件中查找字谜。这是我所拥有的:

try (InputStream is = new URL("http://wiki.puzzlers.org/pub/wordlists/unixdict.txt").openConnection().getInputStream();
     BufferedReader reader = new BufferedReader(new InputStreamReader(is));
     Stream<String> stream = reader.lines()) {
Run Code Online (Sandbox Code Playgroud)

以及字谜的方法:

public boolean isAnagram(String firstWord, String secondWord) {
    char[] word1 = firstWord.replaceAll("[\\s]", "").toCharArray();
    char[] word2 = secondWord.replaceAll("[\\s]", "").toCharArray();
    Arrays.sort(word1);
    Arrays.sort(word2);
    return Arrays.equals(word1, word2);
}
Run Code Online (Sandbox Code Playgroud)

如何使用 Java 8 Stream 检查 unixdict.txt 中的单词是否是字谜?有没有办法将一个词与流中的所有词进行比较?

Hol*_*ger 6

当您想找到所有字谜时,不建议尝试将一个单词与所有其他单词进行比较,因为您最终会将每个单词与所有其他单词进行比较,这称为二次时间复杂度。要处理 1,000 个单词,您将需要一百万次比较,要处理 100,000 个单词,您将需要 10,000,000,000 次比较,依此类推。

你可以改变你的isAnagram方法来为数据结构提供一个查找键,比如HashMap

static CharBuffer getAnagramKey(String s) {
    char[] word1 = s.replaceAll("[\\s]", "").toCharArray();
    Arrays.sort(word1);
    return CharBuffer.wrap(word1);
}
Run Code Online (Sandbox Code Playgroud)

该类CharBuffer包装一个char[]数组并提供必要的equalshashCode方法,而无需复制数组内容,这使得构造一个新的String.

作为旁注,.replaceAll("[\\s]", "")可以简化为.replaceAll("\\s", ""), 两者都会消除所有空格字符,但是您问题的示例输入根本没有空格字符。要删除所有非单词字符,如撇号和与号,您可以使用s.replaceAll("\\W", "").

然后,您可以处理所有单词以在单个线性传递中找到字谜,例如

URL srcURL = new URL("http://wiki.puzzlers.org/pub/wordlists/unixdict.txt");
try(InputStream is = srcURL.openStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    Stream<String> stream = reader.lines()) {

    stream.collect(Collectors.groupingBy(s -> getAnagramKey(s)))
        .values().stream()
        .filter(l -> l.size() > 1)
        .forEach(System.out::println);
}
Run Code Online (Sandbox Code Playgroud)

使用此解决方案,对于较大的单词列表,打印可能会成为更昂贵的部分。因此,您可能会更改流的操作,例如以下打印字谜组合的前十名:

stream.collect(Collectors.groupingBy(s -> getAnagramKey(s)))
    .values().stream()
    .filter(l -> l.size() > 1)
    .sorted(Collections.reverseOrder(Comparator.comparingInt(List::size)))
    .limit(10)
    .forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)