Java 8/JDK8 Потоковые функции Map/Reduce To group List <String> в Map <String, List <String>>

Java 8 вот-вот выйдет... Узнав о потоках, я понял сценарий группировки анаграмм, используя один из новых способов. Проблема, с которой я сталкиваюсь, заключается в том, что я не могу найти способ группировать объекты Strings, используя функции map/reduce. Вместо этого мне пришлось создать аналогичный способ, описанный в http://docs.oracle.com/javase/tutorial/collections/streams/reduction.html.

На основе документации мы можем просто использовать LIST.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);

    // This prints the following:

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

Вместо этого я ищу более простое и более прямое решение, которое использует новые функции map/reduce для накопления результатов в подобной интерфейсной карте. На основе qaru.site/info/46526/... у меня есть следующее:

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. "Исключение в потоке" main "java.lang.IllegalStateException: Дублировать пул ключей", что имеет смысл... Есть ли способ сгруппировать их в аналогичный вывод, как первое решение с groupingBy, но без использования POJO-упаковки значений

Ответ 1

Коллектив с единственным аргументом 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}

EDIT:

Если это было неясно, sortChars - это просто статическая функция, которая выполняет аналогичную функцию для того, что getKey выполняла в первом примере, но из строки в строку:

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