gpt4 book ai didi

java - Lambdas,多个 forEach 带强制转换

转载 作者:IT老高 更新时间:2023-10-28 21:13:55 28 4
gpt4 key购买 nike

需要一些帮助,从我的 StackOverflow 杰出人物那里思考 lambda。

从一个列表中挑选一个列表的标准案例来收集图表深处的一些 child 。 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;
}

请注意,列表本身会以 JSON 的形式发送给客户端,因此不要关注返回的内容。一定是一些我可以减少循环的巧妙方法。

有兴趣看看我的专家同事创造了什么。鼓励采用多种方法。

编辑

findServices 和两个 findChildren 方法返回数组

编辑 - 奖励挑战

“不重要的部分”确实很重要。我实际上需要复制一个仅在 host 实例中可用的值。这似乎毁掉了所有美丽的例子。如何将国家发扬光大?

final ContextInfo info = new ContextInfo(context.getPath());
info.setHostname(host.getName()); // The Bonus Challenge

最佳答案

它的嵌套相当深,但似乎并不特别困难。

第一个观察结果是,如果 for 循环转换为流,则可以使用 flatMap 将嵌套的 for 循环“扁平化”为单个流。 .此操作采用单个元素并在流中返回任意数量的元素。我查了一下,发现StandardServer.findServices()返回 Service 的数组所以我们用 Arrays.stream() 把它变成一个流. (我对 Engine.findChildren()Host.findChildren() 做了类似的假设。

接下来,每个循环中的逻辑执行 instanceof检查和类型转换。这可以使用流建模为 filter操作做instanceof后跟 map简单地转换并返回相同引用的操作。这实际上是一个空操作,但它允许静态类型系统转换 Stream<Container>Stream<Host>例如。

将这些转换应用于嵌套循环,我们得到以下结果:

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;
}

但是等等,还有更多。

final forEach操作稍微复杂一些map转换 Context 的操作变成 ContextInfo .此外,这些只是收集到 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(例如在最终的 map 操作中),所以我会将它重构为一个小辅助方法,该方法采用 Context并返回 ContextInfo .这根本不会缩短代码,但我认为它确实使它更清晰。

更新

但是等等,还有更多。

让我们提取对 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 上的重复过滤。然后是带有类型转换的映射。总共进行了 3 次。似乎其他代码可能需要做类似的事情,所以最好将这部分逻辑提取到辅助方法中。问题是 filter可以更改流中元素的数量(删除不匹配的元素),但不能更改它们的类型。和map可以改变元素的类型,但不能改变它们的数量。可以改变数量和类型吗?是的,是我们的老 friend flatMap再次!所以我们的辅助方法需要获取一个元素并返回一个不同类型的元素流。该返回流将包含单个转换元素(如果它匹配)或者它将是空的(如果它不匹配)。辅助函数如下所示:

<T,U> Stream<U> toType(T t, Class<U> clazz) {
if (clazz.isInstance(t)) {
return Stream.of(clazz.cast(t));
} else {
return Stream.empty();
}
}

(这大致基于一些评论中提到的 C# 的 OfType 构造。)

在此过程中,让我们提取一个方法来创建 ContextInfo :

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());

更好,我认为,我们已经删除了可怕的多行语句 lambda。

更新:奖励挑战

再一次,flatMap是你的 friend 。取流的尾部并将其迁移到最后一个 flatMap在尾部之前。那样 host变量仍在范围内,您可以将其传递给 makeContextInfo已修改为采用 host 的辅助方法也是。

    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());

关于java - Lambdas,多个 forEach 带强制转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25439277/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com