Dav*_*ins 27 java lambda java-8 java-stream
需要一些帮助思考来自我的同伴StackOverflow名人的lambda.
选择列表列表以在图表中深入收集一些孩子的标准情况.什么样的方法可以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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,列表本身将作为客户端发送JSON
,因此请不要关注返回的内容.必须是一些简洁的方法,我可以减少循环.
有兴趣看看我的同事专家创造的东西.鼓励多种方法.
编辑
这findServices
两个findChildren
方法返回数组
编辑 - 奖金挑战
"不重要的部分"确实变得很重要.我实际上需要复制仅在host
实例中可用的值.这似乎毁了所有美丽的例子.如何让国家前进?
final ContextInfo info = new ContextInfo(context.getPath());
info.setHostname(host.getName()); // The Bonus Challenge
Run Code Online (Sandbox Code Playgroud)
Stu*_*rks 36
它是相当深的嵌套但它似乎并不特别困难.
第一个观察是,如果for循环转换为流,嵌套的for循环可以使用"平坦化"为单个流flatMap
.此操作采用单个元素并在流中返回任意数字元素.我查了一下,发现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;
}
Run Code Online (Sandbox Code Playgroud)
但等等,还有更多.
最后的forEach
操作是一个稍微复杂的map
操作,将a转换Context
为a 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());
}
Run Code Online (Sandbox Code Playgroud)
我通常会尝试避免多行lambda(例如在最后的map
操作中),所以我将它重构为一个小的辅助方法,它接受一个Context
并返回一个ContextInfo
.这根本不会缩短代码,但我认为它确实使它更清晰.
UPDATE
但等等,还有更多.
让我们将调用解压缩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()))
// ...
Run Code Online (Sandbox Code Playgroud)
这暴露了重复的过滤,instanceof
然后是使用强制转换的映射.这总共完成了三次.似乎其他代码可能需要做类似的事情,所以将这些逻辑提取到辅助方法中会很好.问题是,filter
可以更改流中的元素数量(丢弃不匹配的元素),但不能更改其类型.并且map
可以改变元素的类型,但它不能改变它们的数量.有什么东西可以改变数量和类型吗?是的,这是我们的老朋友flatMap
了!所以我们的helper方法需要获取一个元素并返回一个不同类型的元素流.该返回流将包含单个转换元素(如果匹配)或它将为空(如果它不匹配).辅助函数如下所示:
<T,U> Stream<U> toType(T t, Class<U> clazz) {
if (clazz.isInstance(t)) {
return Stream.of(clazz.cast(t));
} else {
return Stream.empty();
}
}
Run Code Online (Sandbox Code Playgroud)
(这很基于OfType
一些评论中提到的C#的结构.)
当我们在它的时候,让我们提取一个方法来创建一个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;
}
Run Code Online (Sandbox Code Playgroud)
在这些提取之后,管道看起来像这样:
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());
Run Code Online (Sandbox Code Playgroud)
好吧,我想,我们已经删除了可怕的多行语句lambda.
更新:奖金挑战
再一次,flatMap
是你的朋友.取出溪流的尾部并将其移至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());
Run Code Online (Sandbox Code Playgroud)
Edw*_*rzo 26
这将是我使用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());
Run Code Online (Sandbox Code Playgroud)
在这种方法中,我替换了过滤谓词的if语句.考虑到instanceof
支票可以替换为Predicate<T>
Predicate<Object> isEngine = someObject -> someObject instanceof Engine;
Run Code Online (Sandbox Code Playgroud)
也可以表示为
Predicate<Object> isEngine = Engine.class::isInstance
Run Code Online (Sandbox Code Playgroud)
同样,您的演员表可以替换为Function<T,R>
.
Function<Object,Engine> castToEngine = someObject -> (Engine) someObject;
Run Code Online (Sandbox Code Playgroud)
这几乎是一样的
Function<Object,Engine> castToEngine = Engine.class::cast;
Run Code Online (Sandbox Code Playgroud)
手动将项目添加到for循环中的列表可以用收集器替换.在生产代码中,将a变换Context
为ContextInfo
can(并且应该)的lambda 被提取到单独的方法中,并用作方法引用.
归档时间: |
|
查看次数: |
18338 次 |
最近记录: |