我的代码中重复了以下模式:
class X<T, V>
{
V doTransform(T t) {
return null; // dummy implementation
}
Map<T, V> transform(List<T> item) {
return item.stream().map(x->new AbstractMap.SimpleEntry<>(x, doTransform(x))).collect(toMap(x->x.getKey(), x->x.getValue()));
}
}
要求使用AbstractMap.SimpleEntry是混乱和笨重的。 Linqs使用匿名类型更优雅。
是否有更简单的方法可以使用流来实现这一目标?
提前谢谢。答案 0 :(得分:3)
您可以在值映射器中调用doTransform
:
Map<T, V> transform(List<T> item) {
return item.stream().collect(toMap(x -> x, x -> doTransform(x)));
}
答案 1 :(得分:2)
不幸的是,Java并没有完全等同于C#的匿名类型。
在这种特定情况下,您不需要@Jorn Vernee建议的中间map
操作。相反,您可以在toMap
收集器中执行键和值提取。
然而,当你遇到C#的匿名类型的情况时,你可能会考虑:
Arrays.asList(...)
,List.of(...)
(根据您的使用情况,可能并不总是您想要的)最终,如果真的需要映射到可以包含两种不同类型元素的内容,那么我会坚持使用AbstractMap.SimpleEntry
。
那说,你当前的例子可以简化为:
Map<T, V> transform(List<T> items) {
return items.stream().collect(toMap(Function.identity(),this::doTransform));
}
答案 2 :(得分:1)
在这个具体的例子中,根本不需要进行中间存储:
Map<T, V> transform(List<T> item) {
return item.stream().collect(toMap(x -> x, x -> doTransform(x)));
}
但是如果你需要它,Java 9提供了一种更简单的工厂方法,
Map<T, V> transform(List<T> item) {
return item.stream()
.map(x -> Map.entry(x, doTransform(x)))
.collect(toMap(x -> x.getKey(), x -> x.getValue()));
}
只要您不必处理null
。
您可以在此处使用匿名内部类
Map<T, V> transform(List<T> item) {
return item.stream()
.map(x -> new Object(){ T t = x; V v = doTransform(x); })
.collect(toMap(x -> x.t, x -> x.v));
}
但效率较低。它是一个内部类,它捕获对周围this
的引用,也捕获x
,因此您有两个字段t
和用于捕获x
的合成字段,同样的事情。
后者可以通过使用方法来规避,例如
Map<T, V> transform(List<T> item) {
return item.stream()
.map(x -> new Object(){ T getKey() { return x; } V v = doTransform(x); })
.collect(toMap(x -> x.getKey(), x -> x.v));
}
但它不会增加可读性。
唯一真正的匿名类型是为lambda表达式生成的类型,可用于通过高阶函数存储信息:
Map<T, V> transform(List<T> item) {
return item.stream()
.map(x -> capture(x, doTransform(x)))
.collect(HashMap::new, (m,f) -> f.accept(m::put), HashMap::putAll);
}
public static <A,B> Consumer<BiConsumer<A,B>> capture(A a, B b) {
return f -> f.accept(a, b);
}
但如果您在更复杂的情况下尝试使用它,您很快就会遇到Java类型系统的限制(它仍然不是函数式编程语言)。