Java 8 Streams - 如何比较元素?

Java 8 Streams - how to compare elements?

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

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()) {

以及字谜的方法:

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

如何使用 Java 8 Stream 检查 unixdict.txt 中的单词是否为 anagram?有什么方法可以将一个词与流中的所有词进行比较吗?

我认为您最好的选择可能是使用 multimap 收集器将流转换为 Guava multimap,使用字符串的排序版本作为映射的键。有关如何执行此操作的示例,请参阅 Cleanest way to create a guava MultiMap from a java8 stream。如果您只想要生成的字谜集,则可以使用 multimap.asMap().entrySet().stream()... 根据您的需要过滤和收集结果。

这行得通。我首先在流中完成了所有排序,但这样效率更高。

      InputStream is = new URL("http://wiki.puzzlers.org/pub/wordlists/unixdict.txt")
              .openConnection().getInputStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(is));

      String word = "germany";
      final String sword = sortedWord(word);
      reader.lines().filter(w -> sortedWord(w).compareTo(sword) == 0).forEach(
            System.out::println);

      static String sortedWord(String w) {
         char[] chs = w.toCharArray();
         Arrays.sort(chs);
         return String.valueOf(chs);
      }

一个可能的改进是先过滤单词的长度。你可能想试试这个 word list 因为它有更多的字。

当你想找到所有的变位词时,不建议尝试将一个词与所有其他词进行比较,因为你最终会把每个词与其他词进行比较,这就是所谓的二次time complexity.处理 1,000 个单词,需要进行 100 万次比较,处理 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);
}

class CharBuffer 包装了一个 char[] 数组并提供了必要的 equalshashCode 方法而不复制数组内容,这使其更可取构建一个新的 String.

作为旁注,.replaceAll("[\s]", "") 可以简化为 .replaceAll("\s", ""),两者都会消除所有 space 个字符,但您问题的示例输入没有 space字符在所有。要删除所有非单词字符,例如撇号和符号,您可以使用 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);
}

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

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