首页 > 解决方案 > Java 8:如何将列表流式传输到列表列表中?

问题描述

给定需要排序和分组的对象列表:

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收集器将它们分组到列表地图中:

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

我知道我可以使用 .values() 从sortedWidgetMap中获取一个集合,我猜它是一个有序集合,因为它来自一个有序的地图类型,所以这是我目前的解决方案:

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),或者子列表将按照它们在原始列表中的顺序构建。另外,我认为必须可以单独使用 Stream API 来实现我想要的输出。那么,我怎样才能更好地做到这一点呢?

标签: javajava-8java-stream

解决方案


正如评论中提到的,提到一个非常相似的问题(事实上,我几乎认为它是重复的......),groupBy调用有不同的风格,其中一个允许传入工厂的地图那是要被创造的。

因此,无需将“简单”groupBy调用的结果显式包装到 new 的创建中TreeMap,因为您可以TreeMap直接创建。此地图(及其values()集合!)将按密钥排序。map 的值是列表,它们是使用下游收集器创建的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);
        }

    }
}

推荐阅读