Including elements from unmodified lists in modifi

2019-05-14 09:13发布

问题:

I asked this question about doing the same thing with 1D lists, figuring that it would be easy to apply the answers to 2D lists, but it was not.

Basing my code off of Holger's answer, I came up with this solution:

//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());

I get the compile-time error Cannot infer type argument(s) for <R> map(Function<? super T,? extends R>), however, when I try this.

How is can I perform this function on a 2D list?

Some example input and output:

[[1, 2, 3, 4], [5, 6, 7, 8]] should output [[-1, 0, 1, 4], [3, 4, 5, 8]]

回答1:

You have a problem with the parenthesis. Change this:

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());

To this:

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());

Indentation is important here, to improve code readability. I would reindent the second snippet as follows:

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());

Or even better, I would move the logic that modifies the inner lists to a helper method:

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());
    }
}

And then I would just use this method from the outer stream:

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

Note: there's no need to move the helper method to a new Utility class and you might even let it be a non-static method. In this case, just change the Utility::modifyAllButLast method reference accordingly.



回答2:

Let's fix the errors step by step

Stream#concat concats two streams together, but you concat one is a List<Integer> not a 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());

THEN we can fix the error by return a Stream as below:

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());

BUT the result is not as expected. we expected it returns a List<List<Integer>> but it returns a List<Stream<Integer>> instead. we can collecting the concatinated streams to List<Integer> as below:

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());

But I think the best way in this case is using List#replaceAll that I have answered in your another question which is more expressiveness and faster since Stream is more heavier than Collector.

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());


回答3:

This might not be the most elegant way to concatenate the two lists, but it will do it.

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());