Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/375.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Lambdas,带铸造的多个forEach_Java_Lambda_Java 8_Java Stream - Fatal编程技术网

Java Lambdas,带铸造的多个forEach

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

通过一个列表中的一个列表来收集图形中的一些子元素的标准情况。有什么很棒的方法可以
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
辅助方法,该方法也被修改为Take
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());
奖金挑战的解决方案 灵感来源于@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;
            });
}