Java Lambdas,带铸造的多个forEach
需要我的同事们帮助我思考lambdas 通过一个列表中的一个列表来收集图形中的一些子元素的标准情况。有什么很棒的方法可以Java Lambdas,带铸造的多个forEach,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,需要我的同事们帮助我思考lambdas 通过一个列表中的一个列表来收集图形中的一些子元素的标准情况。有什么很棒的方法可以Lambdas帮助处理这个样板文件 public List<ContextInfo> list() { final List<ContextInfo> list = new ArrayList<ContextInfo>(); final StandardServer server = getServer(); for
Lambdas
帮助处理这个样板文件
public List<ContextInfo> list() {
final List<ContextInfo> list = new ArrayList<ContextInfo>();
final StandardServer server = getServer();
for (final Service service : server.findServices()) {
if (service.getContainer() instanceof Engine) {
final Engine engine = (Engine) service.getContainer();
for (final Container possibleHost : engine.findChildren()) {
if (possibleHost instanceof Host) {
final Host host = (Host) possibleHost;
for (final Container possibleContext : host.findChildren()) {
if (possibleContext instanceof Context) {
final Context context = (Context) possibleContext;
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
list.add(info);
}
}
}
}
}
}
return list;
}
这将是我使用JDK 8流、方法引用和lambda表达式编写的代码版本:
server.findServices()
.stream()
.map(Service::getContainer)
.filter(Engine.class::isInstance)
.map(Engine.class::cast)
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.filter(Host.class::isInstance)
.map(Host.class::cast)
.flatMap(host -> Arrays.stream(host.findChildren()))
.filter(Context.class::isInstance)
.map(Context.class::cast)
.map(context -> {
ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
return info;
})
.collect(Collectors.toList());
在这种方法中,我将替换过滤器谓词的if语句。考虑到检查的实例可以替换为谓词
谓词isEngine=someObject->someObject实例引擎;
也可以表示为
Predicate<Object> isEngine = Engine.class::isInstance
谓词isEngine=Engine.class::isInstance
同样,您的强制转换也可以由函数
替换
函数castToEngine=someObject->(引擎)someObject;
这和
Function<Object,Engine> castToEngine = Engine.class::cast;
函数castToEngine=Engine.class::cast;
在for循环中将项目手动添加到列表中可以替换为收集器。在生产代码中,将上下文
转换为上下文信息
的lambda可以(而且应该)提取到单独的方法中,并用作方法引用。第一次尝试。我要过几年才能发现这本书可读性。必须是一个更好的方法
注意findChildren
方法返回数组,当然可以使用for(N:array)
语法,但不能使用新的Iterable.forEach
方法。必须用数组包装它们。asList
public List<ContextInfo> list() {
final List<ContextInfo> list = new ArrayList<ContextInfo>();
final StandardServer server = getServer();
asList(server.findServices()).forEach(service -> {
if (!(service.getContainer() instanceof Engine)) return;
final Engine engine = (Engine) service.getContainer();
instanceOf(Host.class, asList(engine.findChildren())).forEach(host -> {
instanceOf(Context.class, asList(host.findChildren())).forEach(context -> {
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
list.add(info);
});
});
});
return list;
}
很多管道,毫无疑问是字节码的5倍。一定是更好的方法。嵌套得相当深,但似乎并不特别困难
第一个观察结果是,如果for循环转换为流,则可以使用flatMap
将嵌套的for循环“展平”为单个流。此操作接受单个元素并返回流中任意数量的元素。我查找发现StandardServer.findServices()
返回一个Service
数组,因此我们使用Arrays.stream()
将其转换为流。(我对Engine.findChildren()
和Host.findChildren()
做了类似的假设
接下来,每个循环中的逻辑执行instanceof
检查和强制转换。这可以使用流作为过滤器操作来建模,以执行instanceof
操作,然后执行映射
操作,该操作仅强制转换并返回相同的引用。这实际上是一个无操作,但它允许静态键入系统转换例如,将流
转换为流
将这些转换应用于嵌套循环,我们得到以下结果:
public List<ContextInfo> list() {
final List<ContextInfo> list = new ArrayList<ContextInfo>();
final StandardServer server = getServer();
Arrays.stream(server.findServices())
.filter(service -> service.getContainer() instanceof Engine)
.map(service -> (Engine)service.getContainer())
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.filter(possibleHost -> possibleHost instanceof Host)
.map(possibleHost -> (Host)possibleHost)
.flatMap(host -> Arrays.stream(host.findChildren()))
.filter(possibleContext -> possibleContext instanceof Context)
.map(possibleContext -> (Context)possibleContext)
.forEach(context -> {
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
list.add(info);
});
return list;
}
public List<ContextInfo> list() {
final StandardServer server = getServer();
return Arrays.stream(server.findServices())
.filter(service -> service.getContainer() instanceof Engine)
.map(service -> (Engine)service.getContainer())
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.filter(possibleHost -> possibleHost instanceof Host)
.map(possibleHost -> (Host)possibleHost)
.flatMap(host -> Arrays.stream(host.findChildren()))
.filter(possibleContext -> possibleContext instanceof Context)
.map(possibleContext -> (Context)possibleContext)
.map(context -> {
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
return info;
})
.collect(Collectors.toList());
}
我通常会尽量避免使用多行lambda(例如在最后的映射
操作中),因此我会将其重构为一个小助手方法,该方法采用上下文
,并返回一个上下文信息
。这根本不会缩短代码,但我认为它确实让代码更清晰
更新
但是等等,还有更多
让我们将对service.getContainer()
的调用提取到它自己的管道元素中:
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.filter(container -> container instanceof Engine)
.map(container -> (Engine)container)
.flatMap(engine -> Arrays.stream(engine.findChildren()))
// ...
这暴露了在instanceof
上重复过滤,然后使用强制转换进行映射。这一共进行了三次。似乎其他代码也需要做类似的事情,所以最好将这一点逻辑提取到辅助方法中。问题是过滤器
可以更改e的数量流中的元素(删除不匹配的元素)但是它不能改变它们的类型。而且map
可以改变元素的类型,但是它不能改变它们的数量。有什么东西可以同时改变数量和类型吗?是的,它又是我们的老朋友flatMap
!所以我们的助手方法需要获取一个元素并返回一个不同类型的元素流。返回流将n输入单个强制转换的元素(如果匹配),或者它将为空(如果不匹配)。助手函数如下所示:
<T,U> Stream<U> toType(T t, Class<U> clazz) {
if (clazz.isInstance(t)) {
return Stream.of(clazz.cast(t));
} else {
return Stream.empty();
}
}
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.flatMap(container -> toType(container, Engine.class))
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.flatMap(possibleHost -> toType(possibleHost, Host.class))
.flatMap(host -> Arrays.stream(host.findChildren()))
.flatMap(possibleContext -> toType(possibleContext, Context.class))
.map(this::makeContextInfo)
.collect(Collectors.toList());
在这些提取之后,管道如下所示:
<T,U> Stream<U> toType(T t, Class<U> clazz) {
if (clazz.isInstance(t)) {
return Stream.of(clazz.cast(t));
} else {
return Stream.empty();
}
}
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.flatMap(container -> toType(container, Engine.class))
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.flatMap(possibleHost -> toType(possibleHost, Host.class))
.flatMap(host -> Arrays.stream(host.findChildren()))
.flatMap(possibleContext -> toType(possibleContext, Context.class))
.map(this::makeContextInfo)
.collect(Collectors.toList());
更好,我认为,我们已经删除了可怕的多行语句lambda
更新:奖金挑战
同样,flatMap
是您的朋友。获取流的尾部,并将其迁移到尾部之前的最后一个flatMap
。这样,host
变量仍然在范围内,您可以将其传递给makeContextInfo
辅助方法,该方法也被修改为Takehost
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.flatMap(container -> toType(container, Engine.class))
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.flatMap(possibleHost -> toType(possibleHost, Host.class))
.flatMap(host -> Arrays.stream(host.findChildren())
.flatMap(possibleContext -> toType(possibleContext, Context.class))
.map(ctx -> makeContextInfo(ctx, host)))
.collect(Collectors.toList());
奖金挑战的解决方案
灵感来源于@EdwinDalorzo answer
public List<ContextInfo> list() {
final List<ContextInfo> list = new ArrayList<>();
final StandardServer server = getServer();
return server.findServices()
.stream()
.map(Service::getContainer)
.filter(Engine.class::isInstance)
.map(Engine.class::cast)
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.filter(Host.class::isInstance)
.map(Host.class::cast)
.flatMap(host -> mapContainers(
Arrays.stream(host.findChildren()), host.getName())
)
.collect(Collectors.toList());
}
private static Stream<ContextInfo> mapContainers(Stream<Container> containers,
String hostname) {
return containers
.filter(Context.class::isInstance)
.map(Context.class::cast)
.map(context -> {
ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
info.setHostname(hostname); // The Bonus Challenge
return info;
});
}
公共列表(){
最终列表=新的ArrayList();
最终的StandardServer服务器=getServer();
return server.findServices()
.stream()
.map(服务::getContainer)
.filter(Engine.class::isInstance)
.map(Engine.class::cast)
.flatMap(engine->Arrays.stream(engine.findChildren()))
.filter(Host.class::isInstance)
.map(Host.class::cast)
.flatMap(主机->映射容器)(
Arrays.stream(host.findChildren()),host.getName()
)
.collect(Collectors.toList());
}
专用静态流映射容器(流容器,
字符串(主机名){
返回容器
.filter(Context.class::isInstance)
.map(Context.class::cast)
.map(上下文->{
ContextInfo=newcontextinfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
info.setHostname(hostname);//奖金挑战
退货信息;
});
}
Mind.blow......满地都是。Class:方法引用的有趣用法:
ContextInfo makeContextInfo(Context context) {
// copy to another object -- not the important part
final ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
return info;
}
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.flatMap(container -> toType(container, Engine.class))
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.flatMap(possibleHost -> toType(possibleHost, Host.class))
.flatMap(host -> Arrays.stream(host.findChildren()))
.flatMap(possibleContext -> toType(possibleContext, Context.class))
.map(this::makeContextInfo)
.collect(Collectors.toList());
return Arrays.stream(server.findServices())
.map(service -> service.getContainer())
.flatMap(container -> toType(container, Engine.class))
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.flatMap(possibleHost -> toType(possibleHost, Host.class))
.flatMap(host -> Arrays.stream(host.findChildren())
.flatMap(possibleContext -> toType(possibleContext, Context.class))
.map(ctx -> makeContextInfo(ctx, host)))
.collect(Collectors.toList());
public List<ContextInfo> list() {
final List<ContextInfo> list = new ArrayList<>();
final StandardServer server = getServer();
return server.findServices()
.stream()
.map(Service::getContainer)
.filter(Engine.class::isInstance)
.map(Engine.class::cast)
.flatMap(engine -> Arrays.stream(engine.findChildren()))
.filter(Host.class::isInstance)
.map(Host.class::cast)
.flatMap(host -> mapContainers(
Arrays.stream(host.findChildren()), host.getName())
)
.collect(Collectors.toList());
}
private static Stream<ContextInfo> mapContainers(Stream<Container> containers,
String hostname) {
return containers
.filter(Context.class::isInstance)
.map(Context.class::cast)
.map(context -> {
ContextInfo info = new ContextInfo(context.getPath());
info.setThisPart(context.getThisPart());
info.setNotImportant(context.getNotImportant());
info.setHostname(hostname); // The Bonus Challenge
return info;
});
}