findany()中的嵌套并行流执行随机失败

flseospp  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(276)

以下代码在每10-15次尝试相同输入时抛出illegalargumentexception:

AllDirectedPaths<Vertex, Edge> allDirectedPaths = new AllDirectedPaths<>(graph);
        List<GraphPath<Vertex, Edge>> paths = allDirectedPaths.getAllPaths(entry, exit, true, null);

        return paths.parallelStream().map(path -> path.getEdgeList().parallelStream()
                .map(edge -> {
                    Vertex source = edge.getSource();
                    Vertex target = edge.getTarget();

                    if (source.containsInstruction(method, instructionIndex)) {
                        return source;
                    } else if (target.containsInstruction(method, instructionIndex)) {
                        return target;
                    } else {
                        return null;
                    }
                }).filter(Objects::nonNull)).findAny().flatMap(Stream::findAny)
                .orElseThrow(() -> new IllegalArgumentException("Given trace refers to no vertex in graph!"));

该代码的思想是找到一个顶点,该顶点封装了一条特定的指令(请参见 containsInstruction() ),而顶点至少位于 entryexit 顶点。我知道代码在性能方面不是最优的(路径上的每个中间顶点都会被查找两次),但这并不重要。
输入只是一个跟踪(字符串),从中 method 以及 instructionIndex 可以导出。所有其他变量在这个意义上都是固定的。此外,该方法 containsInstruction() 没有任何副作用。
将'findany()'流操作放在哪里重要吗?我应该把它直接放在过滤操作之后吗?还是嵌套的并行流才是问题所在?

nbewdwxp

nbewdwxp1#

你应该使用 .flatMap(path -> ... ) 并移除 .flatMap(Stream::findAny) .
你的代码不起作用,因为 findAny() 返回一个始终为非null的流,但该流可能 null 元素。
然后,当你申请第二个 findAny() 通过 Optional.flatMap(Stream::findAny) 调用时,最后一次查找操作可能返回空值 Optional ,结果是 null 内部流的元素。
代码应该是这样的:

return paths.stream()
    .flatMap(path -> path.getEdgeList().stream()
        .map(edge -> 
             edge.getSource().containsInstruction(method, instructionIndex) ?
             edge.getSource()                                               :
             edge.getTarget().containsInstruction(method, instructionIndex) ?
             edge.getTarget()                                               :
             null)
        .filter(Objects::nonNull))
    .findAny()
    .orElseThrow(() -> new IllegalArgumentException("whatever"));

旁白:为什么是平行流?管道中似乎没有cpu限制的任务。此外,并行流会产生大量开销。它们在极少数情况下非常有用,即数万个元素和流水线上密集的cpu操作
编辑:如评论中所建议的 map 以及 filter 内流的操作可以安全地转移到外流。通过这种方式,可读性得到了提高,在性能方面没有任何区别:

return paths.stream()
    .flatMap(path -> path.getEdgeList().stream())
    .map(edge -> 
         edge.getSource().containsInstruction(method, instructionIndex) ?
         edge.getSource()                                               :
         edge.getTarget().containsInstruction(method, instructionIndex) ?
         edge.getTarget()                                               :
         null)
    .filter(Objects::nonNull)
    .findAny()
    .orElseThrow(() -> new IllegalArgumentException("whatever"));

另一个注意事项:可能是重构内部代码 map 一种方法 Edge 类会更好,以便逻辑返回源、目标或 null 在已经拥有所有信息的类中。

相关问题