如何在匿名函数中使用计数器?

时间:2016-07-07 20:08:38

标签: java lambda counter final

使用Java 8,我想要两个组合到集合:一个集合是java.util.List;另一个是com.fasterxml.jackson.databind.node.ArrayNode

这是一种非Java 8处理方式:

private void mergeMemberAndRelationshipData(final List<JsonNode> members, final ArrayNode relationships) {

    assert members != null : "members cannot be null";
    assert relationships != null : "relationships cannot be null";

    for (int i = 0; i < members.size(); i++) {

        final ObjectNode relationship = (ObjectNode) relationships.get(i);
        final JsonNode member = members.get(i);

        if (member != null && !member.hasNonNull(ExceptionMapper.errorCodeStr)) {
            relationship.put(FIRST_NAME, this.jsonHelpers.safeGetString(member, FIRST_NAME));
            relationship.put(LAST_NAME, this.jsonHelpers.safeGetString(member, LAST_NAME));
            relationship.put(EMAIL, this.jsonHelpers.safeGetString(member, EMAIL));
            relationship.put(MEMBER_SINCE, this.jsonHelpers.safeGetString(member, MEMBER_SINCE));
        } else {
            LOGGER.error("No corresponding Member, {}, found for Relationship: {}", member, relationship);
        }
    }
}

我想流。我喜欢溪流。好吧,那么,让我们试一试:

private void mergeMemberAndRelationshipData(final List<JsonNode> members, final ArrayNode relationships) {

    assert members != null : "members cannot be null";
    assert relationships != null : "relationships cannot be null";

    int i = 0;

    members.stream().forEach(member -> {
        final ObjectNode relationship = (ObjectNode) relationships.get(i++);

        if (member != null) {
            relationship.put(FIRST_NAME, this.jsonHelpers.safeGetString(member, FIRST_NAME));
            relationship.put(LAST_NAME, this.jsonHelpers.safeGetString(member, LAST_NAME));
            relationship.put(EMAIL, this.jsonHelpers.safeGetString(member, EMAIL));
            relationship.put(MEMBER_SINCE, this.jsonHelpers.safeGetString(member, MEMBER_SINCE));
        } else {
            LOGGER.warn("No corresponding member found for relationship: {}", relationship);
        }
    });
}

但不,那不会起作用:Local variable i defined in an enclosing scope must be final or effectively final。是的,这是有道理的。有点。

好吧,所以我想要流式传输一个集合A,并为A中的每个对象,对集合中的相应对象B执行一些操作。我需要知道索引才能做到这一点。

如何在匿名函数内部使用在匿名函数范围之外声明的计数器?

我可以用创造性的创造性方式解决这个问题:

private void mergeMemberAndRelationshipData(final List<JsonNode> members, final ArrayNode relationships) {

    assert members != null : "members cannot be null";
    assert relationships != null : "relationships cannot be null";

    int[] i = new int[1];
    i[0] = 0;

    members.stream().forEach(member -> {
        final ObjectNode relationship = (ObjectNode) relationships.get(i[0]);
        i[0] = i[0]++;

        if (member != null) {
            relationship.put(FIRST_NAME, this.jsonHelpers.safeGetString(member, FIRST_NAME));
            relationship.put(LAST_NAME, this.jsonHelpers.safeGetString(member, LAST_NAME));
            relationship.put(EMAIL, this.jsonHelpers.safeGetString(member, EMAIL));
            relationship.put(MEMBER_SINCE, this.jsonHelpers.safeGetString(member, MEMBER_SINCE));
        } else {
            LOGGER.warn("No correspodning member foudn for relationship: {}", relationship);
        }
    });
}

或者我可以使用AtomicInteger

AtomicInteger i = new AtomicInteger(0);
...
final ObjectNode relationship = (ObjectNode) relationships.get(i.getAndIncrement());

但这是对AtomicInteger的误用;这意味着线程之间的共享价值。不完全是在这里发生的事情,而且它的重量级太重了,这是一个合适的用途(在我看来,请随意不同意我)。

因此,这是一个非常常见的事情(链接两个有序列表并对其元素进行操作)。它在Java 8之前非常容易(技术上仍然如此,但是你的小学生明白我的观点,我确定)。是否有使用流来实现此目的的Java-8-ey方法?

对于我工作的上下文,我们使用微服务架构方法。这意味着我们有一堆服务负责一组离散的,封装的职责(例如,MemberService处理成员CRUD,而CompanyService处理公司CRUD)。最重要的是,我们有一个编排/身份验证/授权服务,可以进行这些不同的调用,然后将它们拼接在一起。因此,在我粘贴的方法中,这是业务流程层,我获取了两个数据集,现在我想将它们合并在一起。这就是我没有LinkedList或其他类似解决方案的原因。

3 个答案:

答案 0 :(得分:2)

使用Streams可以让您更多地使用函数式编程范例。从这个范例来看,最好在集合上使用Map函数,而不是改变外部状态。

这不是一个精确的重构(例如,没有空成员的日志记录),但它可能是一个伪代码解决方案,可以让你朝着你想要的方向前进。

private void mergeMemberAndRelationshipData(final List<JsonNode> members, final ArrayNode relationships) 
{       
    List<...{SomeJsonObject}...> relationshipList = members.stream()
        .filter(x -> x != null)
        .map(x -> //build individual relationship object)
        .collect(Collectors.toList());

    relationshipList.forEach(x -> //add to ArrayNode); 
}

如果您需要在步骤中迭代这两个集合,另一种方法是:

    private void mergeMemberAndRelationshipData(final List<JsonNode> members, final ArrayNode relationships) 
    {       
        IntStream.range(0, members.size())
            .forEach(i -> 
                { 
                    if (members.get(i) != null)
                        //add data
                    else
                        //perform logging
                });
    }

答案 1 :(得分:2)

你想寻找拉链。 Java最初内置了一个zip实用程序,但它已被删除。以下是使用示例实现的流进行压缩的过去stackoverflow响应:Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip)

除此之外,为了避免在外部保留索引,您可以使用IntStream

final List<String> a = Arrays.asList("a", "b", "c");
final List<String> b = Arrays.asList("1", "2", "3");
IntStream.range(0, Math.min(a.size(), b.size()))
   .mapToObj(i -> a.get(i) + "-" + b.get(i))
   .forEach(System.out::println);

答案 2 :(得分:0)

您不能为此方法使用本地变量。但是,您可以简单地使用类字段:

private int i = 0;

private void mergeMemberAndRelationshipData(final List<JsonNode> members, final ArrayNode relationships) {

    assert members != null : "members cannot be null";
    assert relationships != null : "relationships cannot be null";



    members.stream().forEach(member -> {
        final ObjectNode relationship = (ObjectNode) relationships.get(i++);

        ...
    });
}

这不是线程安全的,所以你需要确保如果你有线程,每个线程都在使用它自己的这个类的实例,或者你有适当的锁定机制。