当前位置: 首页 > 知识库问答 >
问题:

如何用lambda流迭代嵌套列表?

柳镜
2023-03-14

我试图用`stream,特别是嵌套的foreach循环,将以下代码重构为lambda表达式:

public static Result match (Response rsp) {
    Exception lastex = null;

    for (FirstNode firstNode : rsp.getFirstNodes()) {
        for (SndNode sndNode : firstNode.getSndNodes()) {
            try {
                if (sndNode.isValid())
                return parse(sndNode); //return the first match, retry if fails with ParseException
            } catch (ParseException e) {
                lastex = e;
            }
        }
    }

    //throw the exception if all elements failed
    if (lastex != null) {
        throw lastex;
    }

    return null;
}

我的开头是:

rsp.getFirstNodes().forEach().?? // how to iterate the nested 2ndNodes?

共有1个答案

史逸春
2023-03-14

我担心使用流和lambdas,您的性能可能会受到影响。当前的解决方案返回第一个有效且可解析的节点,但是不可能中断对流(如for-each(source))的操作。

而且,因为可以有两个不同的输出(返回结果或抛出异常),所以用单行表达式是不可能做到这一点的。

这是我想出来的。它可能会给你一些想法:

public static Result match(Response rsp) throws Exception {
    Map<Boolean, List<Object>> collect = rsp.getFirstNodes().stream()
            .flatMap(firstNode -> firstNode.getSndNodes().stream()) // create stream of SndNodes
            .filter(SndNode::isValid) // filter so we only have valid nodes
            .map(node -> {
                // try to parse each node and return either the result or the exception
                try {
                    return parse(node);
                } catch (ParseException e) {
                    return e;
                }
            }) // at this point we have stream of objects which may be either Result or ParseException
            .collect(Collectors.partitioningBy(o -> o instanceof Result)); // split the stream into two lists - one containing Results, the other containing ParseExceptions

    if (!collect.get(true).isEmpty()) {
        return (Result) collect.get(true).get(0);
    }
    if (!collect.get(false).isEmpty()) {
        throw (Exception) collect.get(false).get(0); // throws first exception instead of last!
    }
    return null;
}
private static class IntermediateResult {

    private final SndNode node;
    private final Result result;
    private final List<ParseException> exceptions;

    private IntermediateResult(SndNode node, Result result, List<ParseException> exceptions) {
        this.node = node;
        this.result = result;
        this.exceptions = exceptions;
    }

    private Result getResult() throws ParseException {
        if (result != null) {
            return result;
        }
        if (exceptions.isEmpty()) {
            return null;
        }
        // this will show all ParseExceptions instead of just last one
        ParseException exception = new ParseException(String.format("None of %s valid nodes could be parsed", exceptions.size()));
        exceptions.stream().forEach(exception::addSuppressed);
        throw exception;
    }

}

public static Result match(Response rsp) throws Exception {
    return Stream.concat(
                    Arrays.stream(new SndNode[] {null}), // adding null at the beginning of the stream to get an empty "aggregatedResult" at the beginning of the stream
                    rsp.getFirstNodes().stream()
                            .flatMap(firstNode -> firstNode.getSndNodes().stream())
                            .filter(SndNode::isValid)
            )
            .map(node -> new IntermediateResult(node, null, Collections.<ParseException>emptyList()))
            .reduce((aggregatedResult, next) -> {
                if (aggregatedResult.result != null) {
                    return aggregatedResult;
                }

                try {
                    return new IntermediateResult(null, parse(next.node), null);
                } catch (ParseException e) {
                    List<ParseException> exceptions = new ArrayList<>(aggregatedResult.exceptions);
                    exceptions.add(e);
                    return new IntermediateResult(null, null, Collections.unmodifiableList(exceptions));
                }
            })
            .get() // aggregatedResult after going through the whole stream, there will always be at least one because we added one at the beginning
            .getResult(); // return Result, null (if no valid nodes) or throw ParseException
}

编辑2:

通常,在使用findfirst()等终端运算符时,也可以使用惰性求值。因此,只要对需求稍作更改(即返回null而不是抛出异常),就应该可以执行如下所示的操作。但是,带有findfirstflatmap不使用惰性求值(源),因此这段代码尝试解析所有节点。

private static class ParsedNode {
    private final Result result;

    private ParsedNode(Result result) {
        this.result = result;
    }
}

public static Result match(Response rsp) throws Exception {
    return rsp.getFirstNodes().stream()
            .flatMap(firstNode -> firstNode.getSndNodes().stream())
            .filter(SndNode::isValid)
            .map(node -> {
                try {
                    // will parse all nodes because of flatMap
                    return new ParsedNode(parse(node));
                } catch (ParseException e ) {
                    return new ParsedNode(null);
                }
            })
            .filter(parsedNode -> parsedNode.result != null)
            .findFirst().orElse(new ParsedNode(null)).result;
}
 类似资料:
  • 使用JSF 2.0,我需要显示一个表,其中每一行都包含一个打开弹出窗口的链接。我有两种型号:

  • 假设我有一个包含集合的对象,所述集合上的每个元素都包含一个集合,每个集合都包含一个集合。 我想在最深的对象上迭代,并对其应用相同的代码。 命令式的方法是微不足道的,但有没有一种方法来完成这一切? 我可以看到如何从最深的循环中生成lambda: 但我能做得更多吗?

  • 我想迭代嵌套在映射中的列表,数据结构如下所示: Freemarker模板: 详细异常消息: FreeMarker模板错误:“...[...]”左操作数:需要序列或字符串或自动转换为字符串的东西(数字、日期或布尔值),但计算结果为extended_hash(包装器:f.t.simplehash):==>groups 那么,问题出在哪里呢? 附言。 我尝试了而不是,它会抛出一个新的异常堆栈:

  • 我正在寻找帮助,我正在尝试迭代具有订单()的产品,该产品还包含

  • 本文向大家介绍Python中的扁平化嵌套列表迭代器,包括了Python中的扁平化嵌套列表迭代器的使用技巧和注意事项,需要的朋友参考一下 假设我们有一个嵌套的整数列表;我们必须实现一个迭代器以使其扁平化。每个元素可以是整数,也可以是列表。该列表的元素也可以是整数或其他列表。因此,如果输入类似于[[1,1],2,[1,1]],则输出将为[1,1,2,1,1,1] 为了解决这个问题,我们将遵循以下步骤-

  • 我试图遍历一个对象列表,并为每个对象生成一个,并为每个对象生成一个嵌套的。 这是生成第234行异常的代码 更新:注意:第234行在并且有,因为处缺少 templateInputException:模板解析过程中发生错误(模板:“类路径资源[templates/qrcodes.html]”-第234行,第10行) 我已经讨论过这些话题了 在Thymeleaf中如何处理if-else? Thymele