在包含Streams的2D列表的修改列表中包含未修改列表中的元素

时间:2017-05-29 16:51:01

标签: java list java-8 java-stream

我问过this关于对1D列表做同样事情的问题,认为将答案应用到2D列表很容易,但事实并非如此。

根据Holger's回答我的代码,我提出了这个解决方案:

//list is of type ArrayList<List<Integer>>
list.stream()
        .map(l -> Stream.concat(
                l.subList(0, l.size() - 1).stream().map(i -> i - 2),
                Stream.of(l.get(l.size() - 1)).collect(Collectors.toList())
        ))
        .collect(Collectors.toList());

但是,当我尝试这个时,我得到了编译时错误Cannot infer type argument(s) for <R> map(Function<? super T,? extends R>)

如何在2D列表上执行此功能?

一些输入和输出示例:

[[1, 2, 3, 4], [5, 6, 7, 8]]应输出[[-1, 0, 1, 4], [3, 4, 5, 8]]

3 个答案:

答案 0 :(得分:2)

您的括号有问题。改变这个:

list.stream()
    .map(l -> Stream.concat(
            l.subList(0, l.size() - 1).stream().map(i -> i - 2),
            Stream.of(l.get(l.size() - 1)).collect(Collectors.toList())
    ))
    .collect(Collectors.toList());

对此:

list.stream()
    .map(l -> Stream.concat(
            l.subList(0, l.size() - 1).stream().map(i -> i - 2),
            Stream.of(l.get(l.size() - 1))).collect(Collectors.toList())
    )
    .collect(Collectors.toList());

缩进在这里很重要,可以提高代码的可读性。我会重新说明第二个片段如下:

list.stream()
    .map(l -> Stream.concat(
            l.subList(0, l.size() - 1).stream().map(i -> i - 2),
            Stream.of(l.get(l.size() - 1)))
        .collect(Collectors.toList()))
    .collect(Collectors.toList());

或者甚至更好,我会将修改内部列表的逻辑移动到辅助方法:

public final class Utility {
    private Utility() { }

    public static List<Integer> modifyAllButLast(List<Integer> list) {
        return Stream.concat(
                l.subList(0, l.size() - 1).stream().map(i -> i - 2),
                Stream.of(l.get(l.size() - 1)))
            .collect(Collectors.toList());
    }
}

然后我会从外部流中使用这个方法:

list.stream()
    .map(Utility::modifyAllButLast)
    .collect(Collectors.toList());

注意:不需要将辅助方法移动到新的Utility类,甚至可以让它成为非静态方法。在这种情况下,只需相应地更改Utility::modifyAllButLast方法引用。

答案 1 :(得分:2)

让我们逐步修复错误

Stream#concat将两个流汇总在一起,但你连续一个是List<Integer>而不是Stream<Integer>

list.stream().map(l -> Stream.concat(
    l.subList(0, l.size() - 1).stream().map(i -> i - 2),
    Stream.of(l.get(l.size() - 1)).collect(Collectors.toList())
   // it returns List<Integer> ---^
)).collect(Collectors.toList());

那么我们可以通过返回Stream来修复错误,如下所示:

list.stream().map(l -> Stream.concat(
    l.subList(0, l.size() - 1).stream().map(i -> i - 2),
    Stream.of(l.get(l.size() - 1))
)).collect(Collectors.toList());

但是结果与预期不符。我们预计会返回List<List<Integer>>,但会返回List<Stream<Integer>>。我们可以将汇总的流收集到List<Integer>,如下所示:

list.stream().map(main -> 
    Stream.concat(
        main.subList(0, main.size() - 1).stream().map(i -> i - 2),
        Stream.of(main.get(main.size() - 1))           
    ).collect(Collectors.toList())
//   ^--- collecting Stream<Integer> to List<Integer>
).collect(Collectors.toList());

但是我认为在这种情况下最好的方法是使用我在List#replaceAll中回答的another question更多表现力和<强>更快,因为StreamCollector更重。

list.stream().map(main ->
    main.stream().collect(collectingAndThen(toList(), it -> {
        // v--- replacing all items in list except the last one 
        it.subList(0, it.size() - 1).replaceAll(item -> item - 2);
        return it;
    }))
).collect(Collectors.toList());

答案 2 :(得分:0)

这可能不是连接两个列表的最优雅方式,但它会这样做。

list.stream()
    .map(integerList -> {
        List<Integer> toReturn = integerList
                .subList(0, integerList.size() - 1)
                .stream()
                .map(i -> i - 2)
                .collect(Collectors.toList());
        toReturn.add(integerList.get(integerList.size() - 1));
        return toReturn;
    })
    .collect(Collectors.toList());