Java 8: Как передать список в список списков? - PullRequest
2 голосов
/ 18 июня 2019

Приведен список объектов, которые необходимо отсортировать и сгруппировать:

static class Widget {
    // ...
    public String getCode() { return widgetCode; }
    public String getName() { return widgetName; }
}

List<Widget> widgetList = Arrays.asList(
    // several widgets with codes and names
);

Я хочу сгруппировать список в список списков, сгруппированный по widgetCode , с элементами каждого подсписка в порядке, в котором они встречались в исходном списке. Я знаю, что могу сгруппировать их в Карту списков, используя groupingBy Collector:

Map<String,List<Widget>> widgetMap = widgetList.stream()
    .collect(groupingBy(Widget::getCode));

Я не считаю само собой разумеющимся, что ключи отсортированы, поэтому я предпринял дополнительный шаг загрузки всего объекта в тип SortedMap:

SortedMap<String,List<Widget>> sortedWidgetMap = new TreeMap<String,List<Widget>>(
    widgetList.stream()
        .collect(groupingBy(Widget::getCode))
);

Я знаю, что могу получить коллекцию из sortedWidgetMap с помощью .values ​​(), и я предполагаю, что это упорядоченная коллекция, поскольку она исходит из упорядоченного типа карты, так что мое текущее решение:

Collection<List<Widget>> widgetListList = new TreeMap<String,List<Widget>>(
    widgetList.stream()
        .collect(groupingBy(Widget::getCode))
).values();
widgetListList.forEach(System.out::println);  // do something with the data

Пока это работает, но я не уверен, что результирующий widgetListList на самом деле гарантированно будет в правильном порядке (т.е. widgetCode ) или что подсписки будут построены в порядке они были найдены в первоначальном списке. Кроме того, я думаю, что для достижения желаемого результата должна быть возможность использовать только API-интерфейс Stream. Итак, как я могу сделать это лучше?

Ответы [ 2 ]

2 голосов
/ 18 июня 2019

Как упомянуто в комментарии, ссылаясь на вопрос, который очень похож на (на самом деле я почти считал его дубликатом ...), вызов groupBy приходит в разных вариантах, и один из них позволяет передать фабрику для создаваемой карты.

Таким образом, нет необходимости явно оборачивать результат «простого» вызова groupBy в создание нового TreeMap, потому что вы можете создать TreeMap напрямую. Эта карта (и ее values() коллекция!) Будет заказана ключом. Значения карты представляют собой списки, которые создаются с использованием нижестоящего сборщика toList(), который явно говорит, что он будет собирать результаты в порядке встречи .

Таким образом, следующее действительно должно быть простым, правильным и эффективным решением:

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class CollectToListOfList {
    static class Widget {
        String code;
        String name;

        Widget(String code, String name) {
            this.code = code;
            this.name = name;
        }

        public String getCode() {
            return code;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return code + ": " + name;
        }

    }

    public static void main(String[] args) {
        List<Widget> widgetList = Arrays.asList(
            new Widget("0", "A"), 
            new Widget("1", "B"), 
            new Widget("2", "C"),
            new Widget("3", "D"), 
            new Widget("0", "E"), 
            new Widget("1", "F"), 
            new Widget("2", "G"),
            new Widget("3", "H"), 
            new Widget("0", "I"), 
            new Widget("1", "J"));

        Collection<List<Widget>> result = widgetList.stream()
            .collect(Collectors.groupingBy(Widget::getCode, TreeMap::new, Collectors.toList()))
            .values();

        for (List<Widget> list : result) {
            System.out.println(list);
        }

    }
}
0 голосов
/ 18 июня 2019

Отредактировано для исправления предыдущего поста на основе уточнения. Единственное различие между ответом (ответами) от других заключается в том, что результат values ​​() передается в конструктор ArrayList для создания списка списков.

      // Create some data
      Random r = new Random(29);
      String codes = "ABCD";
      List<Widget> widgetList = r.ints(10, 0, 4).mapToObj(
            n -> new Widget(codes.substring(n, n + 1), "N" + i++)).collect(
                  Collectors.toList());

      // Now create the list of lists.

      List<List<Widget>> listofWidgets = new ArrayList<>(
            widgetList.stream().collect(Collectors.groupingBy(Widget::getCode,
                  TreeMap::new,
                  Collectors.toList())).values());

      // Display them
      for (List<?> list : listofWidgets) {
         System.out.println(list);
      }

Класс виджетов


    class Widget {
       String widgetCode;
       String widgetName;

       public Widget(String wc, String wn) {
          widgetCode = wc;
          widgetName = wn;
       }

       public String getCode() {
          return widgetCode;
       }

       public String getName() {
          return widgetName;
       }

       public String toString() {
          return "{" + widgetCode + ":" + widgetName + "}";
       }
    }

...