所以我有这个“有效”的代码(为了简单起见,取代了一些名称):
Map<String, Map<String, ImmutableList<SomeClassA>>> someMap =
someListOfClassA.stream()
.filter(...)
.collect(Collectors.groupingBy(SomeClassA::someCriteriaA,
Collectors.groupingBy(SomeClassA::someCriteriaB, GuavaCollectors.toImmutableList()
)
));
但是,我想更改此代码,以便在通过SomeClassA字段进行分组后,内部集合是SomeClassB。例如,如果类看起来像这样:
假设它们都具有所有args构造函数
class SomeClassA {
String someCriteriaA;
String someCriteriaB;
T someData;
String someId;
}
class SomeClassB {
T someData;
String someId;
}
在某处有一种方法:
public static Collection<SomeClassB> getSomeClassBsFromSomeClassA(SomeClassA someA) {
List<Some List of Class B> listOfB = someMethod(someA);
return listOfB; // calls something using someClassA, gets a list of SomeClassB
}
我想将生成的SomeClass Bs列表展平为
Map<String, Map<String, ImmutableList<SomeClassB>>> someMap =
someListOfClassA.stream()
.filter(...)
. // not sure how to group by SomeClassA fields but result in a list of SomeClassB since one SomeClassA can result in multiple SomeClassB
我不确定这如何适合上面的代码。如何将基于SomeClassB的一堆列表收集到SomeClassA的所有值的单个列表中?如果单个ClassA映射到单个ClassB,我知道如何使用Collectors.mapping使它工作,但由于每个ClassA导致多个ClassB,我不知道如何让它工作。
任何想法都将不胜感激。谢谢!
答案 0 :(得分:4)
使用这样的自定义收藏夹:
Map<String, Map<String, List<SomeClassB>>> someMap =
someListOfClassA.stream()
.filter(...)
.collect(Collectors.groupingBy(SomeClassA::getSomeCriteriaA,
Collectors.groupingBy(SomeClassA::getSomeCriteriaB,
Collectors.mapping(a -> getSomeClassBsFromSomeClassA(a),
flatMapToImmutableList()))));
你可以实现你所追求的目标:
X, Y = np.meshgrid(X, Y)
答案 1 :(得分:3)
虽然我们都在等待Java 9的Collectors.flatMapping
(感谢@shmosel的链接),但您可以编写自己的收藏夹来实现您的目标:
public static <T, D, R> Collector<T, ?, R> flatMapping(
Function<? super T, ? extends Stream<? extends D>> streamMapper,
Collector<? super D, ?, R> downstream) {
class Acc {
Stream.Builder<Stream<? extends D>> builder = Stream.builder();
void add(T t) {
builder.accept(streamMapper.apply(t));
}
Acc combine(Acc another) {
another.builder.build().forEach(builder);
return this;
}
R finish() {
return builder.build()
.flatMap(Function.identity()) // Here!
.collect(downstream);
}
}
return Collector.of(Acc::new, Acc::add, Acc::combine, Acc::finish);
}
此帮助程序方法使用Collector.of
和本地类Acc
来累积由提供的streamMapper
函数返回的流,该函数将原始流的元素作为参数。这些流累积在Stream.Builder
中,将在应用收集器的修整器功能时构建。
在构建这个流流之后,它立即与身份函数进行平面映射,因为我们只想连接流。 (我本可以使用流列表而不是流的流,但我认为Stream.Builder
非常高效且使用率很低。
Acc
还实现了一个组合器方法,该方法将给定的Acc
流的流合并到此Acc
的流构建器中。仅当原始流并行时才会使用此功能。
以下是您在示例中使用此方法的方法:
Map<String, Map<String, ImmutableList<SomeClassB>>> map = someListOfClassA.stream()
.filter(...)
.collect(
Collectors.groupingBy(SomeClassA::getSomeCriteriaA,
Collectors.groupingBy(SomeClassA::getSomeCriteriaB,
flatMapping(
a -> getSomeClassBsFromSomeClassA(a).stream(),
ImmutableList.toImmutableList()))));
编辑:由于@Holger在下面的评论中指出,在累积时不需要将数据缓冲到流构建器中。相反,可以通过在累加器函数中执行展平权来实现平面映射收集器。 Here is @Holger's own implementation of such collector,我在此同意后逐字复制:
public static <T, U, A, R> Collector<T, ?, R> flatMapping(
Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, A, R> downstream) {
BiConsumer<A, ? super U> acc = downstream.accumulator();
return Collector.of(downstream.supplier(),
(a, t) -> {
try (Stream<? extends U> s = mapper.apply(t)) {
if (s != null) s.forEachOrdered(u -> acc.accept(a, u));
}
},
downstream.combiner(), downstream.finisher(),
downstream.characteristics().toArray(new Collector.Characteristics[0]));
}