2

I am trying to do a map operation on a List<Integer>:

list.stream().map(i -> i - 2).collect(Collectors.toList());

Instead of performing the operation on the last element of the list, I would like it to just be passed through. ...Collectors.toList()).add(i) doesn't work, of course, because i is out of scope.

For example, the input list [1, 2, 3, 4] should output [-1, 0, 1, 4]

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
ack
  • 1,181
  • 1
  • 17
  • 36
  • 3
    Extract the sublist until size -1, transform it, then add the last element of the original list to the result. – JB Nizet May 26 '17 at 22:03

10 Answers10

4
List<Integer> result = 
    list.subList(0, list.size() - 1)
        .stream().map(i -> i - 2)
        .collect(Collectors.toCollection(ArrayList::new));
result.add(list.get(list.size() - 1));
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
2

You could stream the original list and limit the stream to exclude the last element, do the mapping and collect to a new ArrayList, then add the last element of the original list to the last position of the new, mutable list:

int size = list.size();

List<Integer> result = list.stream()
    .limit(size - 1)
    .map(i -> i - 2)
    .collect(Collectors.toCollection(() -> new ArrayList(size)));

result.add(list.get(size - 1));

Another way would be to do the mapping and collect all the elements of the original list into the new list and then simply set the last element of the original list in the last position of the new list, overwriting the previous element:

int size = list.size();

List<Integer> result = list.stream()
    .map(i -> i - 2)
    .collect(Collectors.toCollection(() -> new ArrayList(size)));

result.set(size - 1, list.get(size - 1));
fps
  • 33,623
  • 8
  • 55
  • 110
2

You can compose Stream#toList with Stream#collectingAndThen to copy a List, then use List#replaceAll to replace all items except the last one.

List<Integer> result = list.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;
}));

AND you can see the stream api just copy the original List into another List created by stream, so your code can simplify as below:

List<Integer> result = new ArrayList<>(list);
result.subList(0, result.size() - 1).replaceAll(item -> item - 2);
//^--- if you don't want to copy the list, you can just operates on list instead   
Holger
  • 285,553
  • 42
  • 434
  • 765
holi-java
  • 29,655
  • 7
  • 72
  • 83
1

You'll need to alter your solution a little bit and use IntStream#range to index into the list until list.size()-1. Then simply add the last element from the initial list to the new list.

List<Integer> list = Arrays.asList(1, 2, 3, 4);
List<Integer> resultSet = IntStream.range(0,list.size()-1)
                          .map(i -> list.get(i) - 2)
                          .boxed().collect(Collectors.toCollection(ArrayList::new));
resultSet.add(list.get(list.size()-1));
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • 2
    `toList()`: "*There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned*" – steffen May 26 '17 at 22:11
  • @steffen I guess I didn't pay much attention to that, nevertheless I've updated my post to accommodate your comment. – Ousmane D. May 26 '17 at 22:16
  • 1
    A much better solution would be to stream and map a subList. This will have abysmal performance on large linked lists. – JB Nizet May 26 '17 at 22:24
  • @JBNizet Could you post an answer about this? – ack May 26 '17 at 22:27
  • 1
    @AlexQuilliam I already explained it in a comment, but if you really need the code to understand it, you now have it in an answer. – JB Nizet May 26 '17 at 22:33
1

Try this.

List<Integer> list = Arrays.asList(1, 2, 3, 4);
int last = list.size() - 1;
List<Integer> result = IntStream.range(0, list.size())
    .mapToObj(i -> i < last ? list.get(i) - 2 : list.get(i))
    .collect(Collectors.toList());
System.out.println(result);
1

If you don’t want to deal with mutable lists or mutable data structures in general, one way to solve this task, is

List<Integer> result = Stream.concat(
        list.subList(0, list.size()-1).stream().map(i -> i - 2),
        Stream.of(list.get(list.size()-1)))
    .collect(Collectors.toList());
Holger
  • 285,553
  • 42
  • 434
  • 765
0

You have to do that manually:

List<Integer> list = Arrays.asList(1, 2, 3, 4);
List<Integer> mapped = list.stream().map(i -> i - 2).collect(Collectors.toCollection(ArrayList::new));
// Do this only if not empty...
mapped.remove(mapped.size()-1);
mapped.add(list.get(list.size()-1));
steffen
  • 16,138
  • 4
  • 42
  • 81
0

Another perhaps more elegant solution:

List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
Integer last = list.remove(list.size() - 1);
list.replaceAll(i -> i - 2);
list.add(last);
steffen
  • 16,138
  • 4
  • 42
  • 81
0

Another variant, just for fun:

List<Integer> list = Arrays.asList(1, 2, 3, 4);
final int[] count = {0};
List<Integer> collect2 = list.stream()
    .map(i -> (++count[0] < list.size()) ? i - 2 : i)
    .collect(Collectors.toCollection(ArrayList::new));
ing8ar
  • 119
  • 1
  • 5
0

StreamEx provides the extract API for this question:

StreamEx.of(1, 2, 3, 4).mapLastOrElse(i -> i - 2, Function.identity()).toList();
user_3380739
  • 1
  • 14
  • 14