Java 8 Stream Collectors - 用于创建包含多个存储桶中的对象的 Map 的收集器

2023-12-20

下面的代码可以工作并且可读,但在我看来,我有一些中间操作,感觉它们不应该是必要的。我编写了这个简化版本,因为实际代码是一个更大流程的一部分。

我有一个Collection of Widget,每个都有一个名称和多个类型(由常量表示WidgetType枚举)。这些多种类型可以作为Stream<WidgetType>不过,如果有必要,我可以将它们作为其他类型返回。 (由于种种原因,强烈想要的这些将作为Stream<WidgetType>因为这些小部件稍后会在实际代码中使用。)

这些小部件被添加到EnumMap<WidgetType, List<Widget>>后来被翻译成EnumMap<WidgetType, Widget[]>.

If each Widget只有一个WidgetType,这将是一个微不足道的解决方案,但是,由于任何 Widget 都可以有 1 种或多种类型,因此我对以下语法感到困惑Collectors.groupingBy()方法(及其重载)。

这是代码示例,功能齐全,并为我提供了我需要的确切结果。

class StackOverFlowExample {

    private final Map<WidgetType, Widget[]> widgetMap = new EnumMap<>(WidgetType.class);

    public static void main(String[] args) { new StackOverFlowExample(); }

    StackOverFlowExample() {
        Collection<Widget> widgetList = getWidgetsFromWhereverWidgetsComeFrom();

        {
            final Map<WidgetType, List<Widget>> intermediateMap = new EnumMap<>(WidgetType.class);
            widgetList.forEach(w ->
                    w.getWidgetTypes().forEach(wt -> {
                        intermediateMap.putIfAbsent(wt, new ArrayList<>());
                        intermediateMap.get(wt).add(w);
                    })
            );
            intermediateMap.entrySet().forEach(e -> widgetMap.put(e.getKey(), e.getValue().toArray(new Widget[0])));
        }

        Arrays.stream(WidgetType.values()).forEach(wt -> System.out.println(wt + ": " + Arrays.toString(widgetMap.get(wt))));
    }

    private Collection<Widget> getWidgetsFromWhereverWidgetsComeFrom() {
        return Arrays.asList(
                new Widget("1st", WidgetType.TYPE_A, WidgetType.TYPE_B),
                new Widget("2nd", WidgetType.TYPE_A, WidgetType.TYPE_C),
                new Widget("3rd", WidgetType.TYPE_A, WidgetType.TYPE_D),
                new Widget("4th", WidgetType.TYPE_C, WidgetType.TYPE_D)
        );
    }

}

这输出:

TYPE_A: [1st, 2nd, 3rd]
TYPE_B: [1st]
TYPE_C: [2nd, 4th]
TYPE_D: [3rd, 4th]

为了完整起见,这里是Widget类和WidgetType enum:

class Widget {

    private final String       name;
    private final WidgetType[] widgetTypes;

    Widget(String n, WidgetType ... wt) { name = n; widgetTypes = wt; }

    public String getName() { return name; }
    public Stream<WidgetType> getWidgetTypes() { return Arrays.stream(widgetTypes).distinct(); }

    @Override public String toString() { return name; }
}

enum WidgetType { TYPE_A, TYPE_B, TYPE_C, TYPE_D }

欢迎任何有关执行此逻辑的更好方法的想法。谢谢!


恕我直言,关键是转换Widget实例到一个Stream<Pair<WidgetType, Widget>>实例。一旦我们有了它,我们就可以flatMap小部件流并收集结果流。我们当然没有Pair在Java中,所以必须使用AbstractMap.SimpleEntry反而。

widgets.stream()
       // Convert a stream of widgets to a stream of (type, widget)
       .flatMap(w -> w.getTypes().map(t->new AbstractMap.SimpleEntry<>(t, w)))
       // Grouping by the key, and do additional mapping to get the widget
       .collect(groupingBy(e->e.getKey(),
                mapping(e->e.getValue, 
                        collectingAndThen(toList(), l->l.toArray(new Widget[0])))));

附:在这种情况下,IntelliJ 的建议不会使用方法引用来缩短 lambda。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 8 Stream Collectors - 用于创建包含多个存储桶中的对象的 Map 的收集器 的相关文章

随机推荐