从Java 8映射操作返回空元素

Mar*_*ian 25 java map java-8 java-stream

使用Java 8流,当输入Integer没有输出时,映射a 的最佳方法是什么List<Integer>

只需返回null?但现在我的输出列表大小将小于我的输入大小...

    List<Integer> input = Arrays.asList(0,1,2,3);
    List<Integer> output = input.stream()
                                .map(i -> { 
                                    Integer out = crazyFunction(i);
                                    if(out == null || out.equals(0))
                                        return null;
                                    return Optional.of(out);
                                    })
                                .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 50

我不明白为什么你(以及所有的答案)让它变得如此复杂.您有映射操作和过滤操作.所以最简单的方法就是一个接一个地应用这些操作.除非你的方法已经返回Optional,否则没有必要处理Optional.

input.stream().map(i -> crazyFunction(i))
              .filter(out -> out!=null && !out.equals(0))
              .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

它可以简化为

input.stream().map(context::crazyFunction)
              .filter(out -> out!=null && !out.equals(0))
              .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

但是你似乎有更多的理论问题,关于List生成什么样的类型,一个是缺少值的占位符,另一个是与输入列表不同的大小.

简单的答案是:不生成列表.一个List不以自身为目的,所以你应该考虑什么样的操作,你需要这个名单(或内容),并申请经营权作为流的终端操作.然后你得到答案,因为操作规定是否应该过滤掉缺席值或用特殊值表示(以及必须是什么值).

对于不同的操作,它可能是一个不同的答案......

  • @Martin Magakian:你确定吗?我没有看到任何自动过滤的`null`文件,既没有在`map`函数也没有`toList`收集器.我的机器上的快速测试显示`map`功能按预期方式1:1工作.如果你想要一个不同大小的结果列表,你必须明确地`过滤`或`flatMap`. (5认同)
  • 这很简单。此外,如果您只想过滤 null 返回值,那么我们也可以将 `.filter(out -&gt; out != null)` 简化为 `.filter(Objects::nonNull)` 。 (3认同)

Stu*_*rks 31

替换map呼叫flatMap.该map操作为每个输入值产生一个输出值,而该flatMap操作产生每个输入值的任意数量的输出值 - 包括零.

最简单的方法可能是更换支票,如下所示:

List<Integer> output = input.stream()
                            .flatMap(i -> { 
                                Integer out = crazyFunction(i);
                                if (out == null || out.equals(0))
                                    return Stream.empty();
                                else
                                    return Stream.of(out);
                                })
                            .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

进一步的重构可能会改变crazyFunction,让它返回Optional(可能OptionalInt).如果你打电话给它map,结果是a Stream<OptionalInt>.然后你需要flatMap该流来删除空的选项:

List<Integer> output = input.stream()
    .map(this::crazyFunctionReturningOptionalInt)
    .flatMap(o -> o.isPresent() ? Stream.of(o.getAsInt()) : Stream.empty())
    .collect(toList());
Run Code Online (Sandbox Code Playgroud)

这个结果flatMap是一个Stream<Integer>包装ints的结果,但这是可以的,因为你要将它们发送到一个List.如果你不打算盒子int值成List,你可以转换Stream<OptionalInt>IntStream使用下列内容:

flatMapToInt(o -> o.isPresent() ? IntStream.of(o.getAsInt()) : IntStream.empty())
Run Code Online (Sandbox Code Playgroud)

有关处理选项流的进一步讨论,请参阅此问题及其答案.

  • 首先不创建空项表示本身显然是一个优势。 (2认同)
  • @Eran我认为主要是风格问题.如果你有一个有时不想返回结果的映射操作,使用`flatMap`返回零或一个值可能是有意义的.将"Optional"值返回到流后跟`filter(Optional :: isPresent).map(Optional :: get)`工作并且简单明了,但可能与may-return-no-value映射器不能很好地对齐.但它肯定是一种价值技术. (2认同)
  • @StuartClark:如果您想知道哪个更快,请测量,不要猜测,尤其是不要基于对流工作原理的基本误解进行猜测。没有“再次解析流中的值”这样的事情。 (2认同)
  • @Holger,@StuarClark:我测量了 `map(...).filter(out -&gt; out!=null &amp;&amp; !out.equals(0))` 和 `flatMap(...)`。不同Listsizes的平均结果(其中比率的第一部分是filter-method的测量时间和flatMap-method的第二部分,都是近似值)-&gt; 10.000 List-Elements = 120ms:10ms, 100.000 = 130ms :60ms, 1.000.000= 160ms:100ms。10.000.000=500ms:5sec 时的大惊喜。所以直觉是,Holgers 的方法在像 10.000.000 个元素这样的非常大的列表中执行。 (2认同)