简体   繁体   English

如何使用 Java 8 流平面映射多个元素?

[英]How to flatmap multiple elements with Java 8 streams?

I have a class我有一堂课

public class Step
      {
         boolean isActive;
         String name;
      }

I have a Collection of type Steps.我有一个 Steps 类型的集合。 Without using streams this is what I have currently不使用流,这就是我目前所拥有的

  StringBuilder stringBuilder = new StringBuilder();
  for (Step step : steps)
  {
     List<String> nextStepNames = getNextStepNames(step);
     List<String> conditions = getConditions(step);
     for (int i = 0; i < nextStepNames.size(); i++)
     {
        stringBuilder.append("If ").append(step.getName()).append("is active, and condition (").append(conditions.get(i)).append(") is true, then move to ").append(nextStepNames.get(i)).append("\n");
     }
  }

If my step collection contains stepA, StepB and stepC, then this is my output:如果我的步骤集合包含 stepA、StepB 和 stepC,那么这是我的输出:

If stepA is active, and condition (c1A) is true, then move to step1A
If stepA is active, and condition (c2A) is true, then move to step2A
If stepA is active, and condition (c3A) is true, then move to step3A
If stepB is active, and condition (c1B) is true, then move to step1B
If stepB is active, and condition (c2B) is true, then move to step2B
If stepB is active, and condition (c3B) is true, then move to step3B
If stepC is active, and condition (c1C) is true, then move to step1C
If stepC is active, and condition (c2C) is true, then move to step2C
If stepC is active, and condition (c3C) is true, then move to step3C

The nextStepNames and conditions list are the same size and the indexes in the lists correspond to each other. nextStepNamesconditions列表大小相同,列表中的索引相互对应。

I haven't been able to convert this code into streams.我无法将此代码转换为流。 I not sure if its possible.我不确定它是否可能。

Java lacks the abilities to solve the problem efficiently in an elegant pure functional style. Java 缺乏以优雅的纯函数式风格高效解决问题的能力。

However, you could try something like但是,您可以尝试类似

    str = steps.stream()
        .map(step ->
            IntStream
                .range(0, getNextStepNames(step).size())
                .mapToObj(i -> Stream.of(
                    "If ",
                    step.getName(),
                    " is active, and condition (",
                    getConditions(step).get(i),
                    ") is true, then move to ",
                    getNextStepNames(step).get(i),
                    "\n"))
                .flatMap(Function.identity())
        )
        .flatMap(Function.identity())
        .collect(Collectors.joining());

This quite inefficient due to the repeated evaluation of getNextStepNames and getConditions and the inability to allocate the complete output buffer in advance.由于getNextStepNamesgetConditions的重复评估以及无法提前分配完整的输出缓冲区,这非常低效。

Of course you could try to mitigate this by using third party libraries, but imho it's not worth the effort.当然,您可以尝试通过使用第三方库来缓解这种情况,但恕我直言,这不值得付出努力。

Your solution is more efficient and much easier to understand and maintain.您的解决方案更高效,更易于理解和维护。 You cold further improve this by initializing the StringBuilder with a size that is equal or a little bit greater than the final output size.您可以通过使用等于或StringBuilder最终输出大小的大小初始化StringBuilder进一步改进这一点。

One step closer to that could be :更接近这一点可能是:

for (Step step : steps) {
    List<String> nextStepNames = getNextStepNames(step);
    List<String> conditions = getConditions(step);
    IntStream.range(0, nextStepNames.size())
            .forEach(i -> stringBuilder.append("If ")
                    .append(step.getName())
                    .append("is active, and condition (")
                    .append(conditions.get(i))
                    .append(") is true, then move to ")
                    .append(nextStepNames.get(i)).append("\n"));
}

Try this:尝试这个:

String output = Arrays.stream(steps) // if it's an array or steps.stream() for a list
    .flatMap(step -> IntStream.range(0, getNextStepNames(step).size())
        .mapToObj(i -> String.format(
            "If %s is active, and condition (%s) is true, then move to %s",
            step.getName(),
            getConditions(step).get(i),
            getNextStepNames(step).get(i))))
    .collect(Collectors.joining("\n"));

Our initial stream only contains three elements (step A, B and C), so for each element, we need another stream.我们的初始流仅包含三个元素(步骤 A、B 和 C),因此对于每个元素,我们需要另一个流。 We create an IntStream with all valid indexed of both lists.我们创建一个 IntStream ,其中包含两个列表的所有有效索引。 We're mapping them to a string, getting the elements from the two methods.我们将它们映射到一个字符串,从两个方法中获取元素。 I used String.format , but this of course can be replaced by a StringBuilder or simple string concatenation.我使用了String.format ,但这当然可以用StringBuilder或简单的字符串连接替换。

At this point, we have streams within streams.在这一点上,我们有流中的流。 We need to flattenize it to a single stream, simply by calling flatMap .我们需要将其扁平flatMap单个流,只需调用flatMap

At last, we can join all elements using \\n as glue.最后,我们可以使用\\n作为胶水连接所有元素。


It's a shame that getNextStepNames and getConditions both seem related, but yet are separate.遗憾的是getNextStepNamesgetConditions看起来都相关,但又是分开的。 But that's another story.但那是另一个故事了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM