Java 8流函数将一组变位词列表分组为列表映射

10

Java 8即将发布...在学习Streams时,我遇到了一个关于使用其中一种新方法对变位词进行分组的场景。我遇到的问题是我找不到使用map/reduce函数来分组字符串对象的方式。相反,我不得不创建一个与聚合操作-规约文档中所述类似的方式。

根据文档,我们可以简单地使用:

LIST<T>.stream().collect(Collectors.groupingBy(POJO::GET_METHOD))

这样,Collectors.groupingBy() 方法将根据使用的方法聚合地图的键。然而,这种方法似乎过于繁琐,无法简单地包装成一个字符串表示。

public class AnagramsGrouping {
    static class Word {
        public String original;

        public Word(String word) {
            original = word;
        }

        public String getKey() {
            char[] characters = input.toCharArray();
            Arrays.sort(characters);
            return new String(characters);
        }

        public String toString() {
            return original;
        }
    }

    public static void main(String[] args) {
        List<Word> words = Arrays.asList(new Word("pool"), new Word("loop"),
                new Word("stream"), new Word("arc"), new Word("odor"),
                new Word("car"), new Word("rood"), new Word("meats"),
                new Word("fires"), new Word("fries"), new Word("night"),
                new Word("thing"), new Word("mates"), new Word("teams"));

        Map<String, List<Word>> anagrams = words.stream().collect(
                Collectors.groupingBy(Word::getKey));

        System.out.println(anagrams);
    }
}

这将打印以下内容:

{door=[odor, rood], acr=[arc, car], ghint=[night, thing],
 aemrst=[stream], efirs=[fires, fries], loop=[pool, loop],
 aemst=[meats, mates, teams]}

相反,我正在寻找一个更简单、更直接的解决方案,使用新的map/reduce函数将结果累积到类似接口 Map<String,List<String>中。根据如何将List转换为Map,我有以下内容:

List<String> words2 = Arrays.asList("pool", "loop", "stream", "arc",
        "odor", "car", "rood", "meats", "fires", "fries",
        "night", "thing", "mates", "teams");

words2.stream().collect(Collectors.toMap(w -> sortChars(w), w -> w));

但是这段代码会导致键冲突,因为它是1-1的Map。

Exception in thread "main" java.lang.IllegalStateException: Duplicate key pool

这是有道理的...是否有一种方法可以将它们分组成与第一个解决方案类似的输出,但不使用POJO包装值?

2个回答

19

单参数的groupingBy收集器恰好可以完成你想要做的事情。它对输入进行分类,你已经使用sortChars(或者在之前的示例中使用getKey)进行了分类。被归类到相同键下的每个流值都会被放入一个列表中,该列表是映射的值。因此我们有:

Map<String, List<String>> anagrams =
    words2.stream().collect(Collectors.groupingBy(w -> sortChars(w)));

输出结果

{door=[odor, rood], acr=[arc, car], ghint=[night, thing], aemrst=[stream],
efirs=[fires, fries], loop=[pool, loop], aemst=[meats, mates, teams]}

你还可以使用方法引用:

Map<String, List<String>> anagrams =
    words2.stream().collect(Collectors.groupingBy(GroupingAnagrams::sortChars));

如果你想对值进行其他操作而不是构建一个列表,请使用 groupingBy 的多参数重载以及“downstream”收集器。例如,若要计算单词数量而不是构建列表,请执行以下操作:

Map<String, Long> anagrams =
    words2.stream().collect(
        Collectors.groupingBy(GroupingAnagrams::sortChars, Collectors.counting()));

这导致:

{door=2, acr=2, ghint=2, aemrst=1, efirs=2, loop=2, aemst=3}

编辑:

如果不清楚,sortChars只是一个静态函数,执行类似于第一个示例中的getKey的功能,但从字符串到字符串:

public static String sortChars(String input) {
    char[] characters = input.toCharArray();
    Arrays.sort(characters);
    return new String(characters);
}

0
您可以使用 toMap 方法,其带有四个参数并可分别指定:键类型、值类型、用于具有相同键的值的合并函数以及要将结果插入其中的 Map 的特定实现。
在这种情况下,您可以选择:
  • 键 - int[] - 单词字符代码点的排序数组;
  • 值 - List<String> - 一组字谜;
  • 合并函数 - 将两个列表合并为一个;
  • 映射 - 具有比较两个 int[] 数组的比较器的 TreeMap
List<String> words = List.of("pool", "loop", "stream", "arc", "odor", "car",
        "rood", "meats", "fires", "fries", "night", "thing", "mates", "teams");

Map<int[], List<String>> anagrams = words.stream()
        .collect(Collectors.toMap(
                // key - a sorted array of character code points
                word -> word.codePoints().sorted().toArray(),
                // value - a list of anagrams
                word -> new ArrayList<>(List.of(word)),
                // merge elements of two lists
                (list1, list2) -> {
                    list1.addAll(list2);
                    return list1;
                },
                // comparator that compares two int[] arrays
                () -> new TreeMap<>(Arrays::compare)));

// output
anagrams.forEach((k, v) -> System.out.println(v.get(0) + "=" + v));

输出:

arc=[arc, car]
stream=[stream]
meats=[meats, mates, teams]
odor=[odor, rood]
fires=[fires, fries]
night=[night, thing]
pool=[pool, loop]

另请参阅:如何检查一个单词是否有一个回文异序词?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接