Java 8流收集器-用于使用多个存储桶中的对象创建映射的收集器 [英] Java 8 Stream Collectors - Collector to create a Map with objects in multiple buckets
问题描述
以下代码可以工作并且可读,但是在我看来,我有一些中间操作,觉得它们不必要.我编写了此简化版本,因为实际代码是更大过程的一部分.
The following code works and is readable but it seems to me I have intermediate operations that feel like they shouldn't be necessary. I've written this simplified version as the actual code is part of a much larger process.
我有一个Collection
Widget
,每个都有一个名称和多种类型(由Stream<WidgetType>
进行获取,但是,如果有必要,我可以将其作为其他类型返回. (出于各种原因,强烈希望将它们作为Stream<WidgetType>
返回,因为稍后在实际代码中如何使用这些小部件.)
I've got a Collection
of Widget
, each with a name and multiple types (indicated by constants of the WidgetType
enum). These multiple types are gettable as a Stream<WidgetType>
though, if necessary, I could return those as some other type. (For various reasons, it is strongly desirable that these be returned as a Stream<WidgetType>
because of how these widgets are used later in the actual code.)
这些小部件将添加到EnumMap<WidgetType, List<Widget>>
中,随后将其翻译为EnumMap<WidgetType, Widget[]>
.
These widgets are added to an EnumMap<WidgetType, List<Widget>>
which is, later, translated into an EnumMap<WidgetType, Widget[]>
.
如果每个Widget
仅具有一个WidgetType
,这将是一个微不足道的解决方案,但是,由于任何Widget都可以具有1个或多个类型,因此,我使用Collectors.groupingBy()
方法的语法对自己进行了遍历(及其超载).
If each Widget
only had a single WidgetType
, this would be a trivial solve but, since any Widget could have 1 or more types, I am tripping all over myself with the syntax of the Collectors.groupingBy()
method (and its overloads).
这又是一个代码示例,功能齐全,可以为我提供所需的确切结果.
Here's the code example, again, fully functional and gives me the exact result I need.
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
枚举:
For completeness sake, here's the Widget
class and the 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 }
欢迎提出任何更好的方法来执行此逻辑的想法.谢谢!
Any ideas on a better way to execute this logic are welcome. Thanks!
推荐答案
恕我直言,关键是将Widget
实例转换为Stream<Pair<WidgetType, Widget>>
实例.一旦有了这些,就可以flatMap
一系列小部件并收集生成的流.当然,在Java中我们没有Pair
,因此必须改用AbstractMap.SimpleEntry
.
IMHO, the key is to convert a Widget
instance to a Stream<Pair<WidgetType, Widget>>
instance. Once we have that, we can flatMap
a stream of widgets and collect on the resulting stream. Of course we don't have Pair
in Java, so have to use AbstractMap.SimpleEntry
instead.
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])))));
P.S.在这种情况下,IntelliJ的建议不会用方法引用缩短lambda.
P.S. this is an occasion where IntelliJ's suggestion doesn't shorten a lambda with method reference.
这篇关于Java 8流收集器-用于使用多个存储桶中的对象创建映射的收集器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!