Reputation: 10949
I have the following code:
List<String> list= Arrays.asList("a","b","c");
list.stream()
.map(x->{
//if(isLast()) return "last";
return x;
})
.forEach(System.out::println);
I want to change the last item in the list with "last" so the program will return something like this
a
b
last
NOTE: I know I can do this very easy without streams, but I'm interested if this can be achieved with streams.
NOTE2: I provided a simple example, so I can easily understand the stream concept
Upvotes: 4
Views: 9468
Reputation: 9944
How about this:
<T> Stream<T> replaceLast(List<T> items, T last) {
Stream<T> allExceptLast = items.stream().limit(items.size() - 1);
return Stream.concat(allExceptLast, Stream.of(last));
}
The method makes two separate streams, one containing all except the last item of the input list, and a second containing only the replacement final item. Then it returns the concatenation of those two streams.
Example invocation:
>>> List<String> items = Arrays.asList("one", "two", "three");
>>> replaceLast(items, "last").forEach(System.out::println);
one
two
last
Thinking about it, if using Stream.limit
is uncomfortable, Stream<T> allExceptLast = items.subList(0, items.size() - 1).stream();
is also possible.
Upvotes: 12
Reputation: 25960
I don't expect this answer to be upvoted because I won't directly answer your question, but I think there must be someone to clearly say this.
Stack Overflow is not a place where we should try giving answers to any question. Sometimes the best thing to do is to explain the OP that his approach is wrong and show him a better approach.
In this particular case, I think streams are not what you are looking for. You should see streams as sequences and the way you process it is usually element by element, there is most of the time no lookback or look forward. A stream could possibly be infinite or really huge, and you don't know when it will finish.
On the contrary, a list is a finite data structure with a given size. The notion of last element is well defined. The only reason other people have been able to give you an answer is that they are using the fact they know the stream is coming from a list. With a general stream that could come from anywhere, you would not be able to do that.
The stream approach to such problem would be to use an iterator, which is completely agnostic to the origin of the stream.
Stream<String> stream = someStream;
Iterator<String> it = stream.iterator();
while (it.hasNext()) {
String next = it.next();
System.out.println(it.hasNext() ? "last" : next);
}
This is not pretty, but this is the right streamy approach. Now, you have a list so you can also do the traditional for
loop. However, this iterator loop is actually the best way to do it with a unknown list. The for
loop will only work well for random access lists and will be slow with the others.
Upvotes: 8
Reputation: 425043
I want to change the last item in the list with "last"
You don't need (or want) to use a stream. Just set()
the last element directly:
list.set(list.size() - 1, "last");
If for some reason (not stated) you absolutely must use a stream, then you have a problem because streams don't know where their current positions is relative to the rest of the stream.
You can introduce a counter into the lambda by circumventing the "effectively final" requirement of referenced variables like this:
int[] index = {0}; // array is effectively final (contents may change!)
list.stream()
.map(x -> ++index[0] == list.size() ? "last" : x)
.forEach(System.out::println);
Don't do this unless you're sure the lambda won't be used elsewhere, because while lists stream their elements sequentially, other streams may stream in parallel, in which case your special action will happen at an indeterminate position in the stream.
Upvotes: 3