10

Java 8 がリリースされようとしています... Streams について学習しているときに、新しい方法の 1 つを使用してアナグラムをグループ化するというシナリオに入りました。私が直面している問題は、map/reduce 関数を使用して Strings オブジェクトをグループ化する方法が見つからないことです。代わりに、 Aggregate Operations - Reductionに記載されているのと同様の方法を作成する必要がありました。

ドキュメントに基づいて、次を簡単に使用できます。

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

したがって、Collectors.groupingBy()使用される方法に基づいてマップのキーが集約されます。ただし、このアプローチは、単純な String プレゼンテーションをラップするにはあまりにも面倒です。

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

これは理にかなっています...groupingBy値をラップするPOJOを使用せずに、最初のソリューションと同様の出力にそれらをグループ化する方法はありますか?

4

2 に答える 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と「ダウンストリーム」コレクターを使用してください。たとえば、リストを作成する代わりに単語を数えるには、次のようにします。

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);
}
于 2014-02-24T04:40:58.390 に答える
0

4 つのパラメーターを持つメソッドを使用toMapして、キーの型、値の型、同じキーを持つ値のマージ関数、およびMap結果が挿入される の特定の実装を個別に指定できます。

この場合、次を選択できます。

  • key - int[]- 単語の文字コード ポイントのソートされた配列。
  • value - List<String>- アナグラムのリスト。
  • マージ機能 - 2 つのリストを 1 つに。
  • map - 2 つの配列TreeMapを比較するコンパレータを使用。int[]
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]

参照:単語に回文であるアナグラムがあるかどうかを確認するにはどうすればよいですか?

于 2021-04-14T22:33:03.220 に答える