Reverse order a Stream in Java
source link: https://www.dontpanicblog.co.uk/2020/10/23/reverse-order-stream/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Reverse order a Stream in Java
How do you reverse order a Stream in Java? There’s no obvious Stream.reverse() method. Is this an oversight in the API? Is there a workaround?
It’s not an oversight. It simply not possible to reverse order a Stream because of how it works. A Stream is not a data structure. It’s an API to perform operations on a data structure. It’s a bit like an Iterator but with richer functionality. In fact, may Stream operations are built on top of the Iterator and Spliterator interfaces. These allow you to traverse a source (usually a Collection) sequentially starting from the beginning. There’s no way to access elements randomly like List.get() and there’s no way to iterate from the end like Deque.getLast(). This allows no mechanism to reverse order a Stream unless we collect it to an intermediate data structure.
There’s also the question how how you’d reverse the order of an infinite length Stream such as Random.ints() or the result of Stream.iterate(). If you were to reverse an infinite length stream, what would be the first element?
Reverse after after collect()
The simplest way to get round this is to collect() the Stream to a data structure (for example, a List) and then reverse the collected data structure.
As an example, here’s a method that takes a String of HTML and returns a Stream of HTML tag names:
private
static
Stream<String> tagsStream(String tags) {
Matcher tagMatcher = Pattern.compile(
"<(\\w+).*?\>"
).matcher(tags);
return
tagMatcher.results()
.map(result -> result.group(
1
));
}
We want to use the resulting Stream to generate the corresponding closing tags. This involves:
- Wrapping each tag name with </close tag> angle brackets;
- Reversing the order of tags
The steps can be performed in any order.
We can create a List of closing tags collecting the Stream to a List and then reversing the result:
public
static
String closeTagsReverseAfterCollect(String openTags) {
List<String> closeTags = tagsStream(openTags)
.map(tag ->
"</"
+ tag +
">"
)
.collect(Collectors.toList());
Collections.reverse(closeTags);
return
String.join(
""
, closeTags);
}
Use an intermediate data structure
A more elegant solution is to write a custom Collector that returns a reversed list. It’s fairly simple and leverages the existing Collectors.toList() Collector:
private
static
<T> Collector<T, ?, List<T>> toReversedList() {
return
Collectors.collectingAndThen(Collectors.toList(), list -> {
Collections.reverse(list);
return
list;
});
}
This simplifies our Stream based method to close HTML tags to look like this:
public
static
String closeTagsReversedListCollector(String openTags) {
List<String> closeTags = tagsStream(openTags)
.map(tag ->
"</"
+ tag +
">"
)
.collect(toReversedList());
return
String.join(
""
, closeTags);
}
The result of the collect() is a List in reverse order – no further manipulation required.
Reverse order on collect()
Finally, if you absolutely must reverse the order of the Stream so that you can perform further operations on it, a Collector is no use. collect() is a terminating operation so you can’t perform further Stream operations unless you create a new Stream from the resulting List.
A static method to reverse a Stream requires an intermediate data structure. I’ve used a LinkedList (implements Deque) as it allows us to easily create a reversed data structure by iteratively push()ing each element to the head of the list. The last element push()ed will be the first element of the list.
private
static
<T> Stream<T> reverse(Stream<T> stream) {
LinkedList<T> stack =
new
LinkedList<>();
stream.forEach(stack::push);
return
stack.stream();
}
To demonstrate, in this example we reverse the order of tags names and then create closing tags as a subsequent Stream operation.
public
static
String closeTagsReverseStream(String openTags) {
List<String> closeTags = reverse(tagsStream(openTags))
.map(tag ->
"</"
+ tag +
">"
)
.collect(Collectors.toList());
return
String.join(
""
, closeTags);
}
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK