Functional Programming in Java, Second Edition: Additional subchapter on building one's own collector?

I have been thinking about how to collect “two adjacent elements” in a stream, for example transform a stream of Long
into a stream of Pair<Long,Long>
(where Pair<A,B>
is a little record that does just what it says). I only came up with the idea of a stateful lambda to be used inside a Stream.map()
that buffers every second element and sends an Optional<Pair>
rightwards that can then be filtered by its not-emptyness" (Good idea? It won’t support parallel streams for sure; this could also be used for illustration in Chapter 12 - “Avoid Side-Effects in Functional Pipelines”)
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Experimental {
record PairOfInt(Integer a, Integer b) {
@Override
public String toString() {
return "(" + a + ", " + b + ")";
}
}
// Problems:
// 1) We lost the last element in a stream with an odd number of elements
// 1) If the stream is run "in parallel" anything can happen here.
// It would definitely be necessary to synchronize the "stash"
// 2) Is there a way to make sure and make evident in code that
// a stream cannot be run in parallel so that the next developer
// doesn't try something stupid?
public Function<Integer, Optional<PairOfInt>> buildPairBuilder() {
List<Integer> stash = new ArrayList<>(1);
return (x) -> {
synchronized (stash) {
if (stash.isEmpty()) {
stash.add(x);
return Optional.empty();
} else {
return Optional.of(new PairOfInt(stash.remove(0), x));
}
}
};
}
private static String stringify(List<PairOfInt> pairs) {
return pairs.stream()
.map(PairOfInt::toString)
.collect(Collectors.joining(", "));
}
// Behaves well, prints out
// (0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11), (12, 13), (14, 15), (16, 17), (18, 19) ...
@Test
public void runStreamSequentially() {
var pairBuilder = buildPairBuilder();
List<PairOfInt> pairs =
IntStream.rangeClosed(0, 33)
.boxed()
.map(pairBuilder)
.filter(Optional::isPresent)
.map(Optional::orElseThrow)
.toList();
System.out.println(stringify(pairs));
}
// Behaves badly, prints out for example
// (0, 1), (2, 3), (25, 4), (31, 5), (7, 6), (10, 11), (26, 13), (12, 14), ...
@Test
public void runStreamParallel() {
var pairBuilder = buildPairBuilder();
List<PairOfInt> pairs =
IntStream.rangeClosed(0, 33)
.parallel() // **** DANGER, WILL ROBINSON! ****
.boxed()
.map(pairBuilder)
.filter(Optional::isPresent)
.map(Optional::orElseThrow)
.toList();
System.out.println(stringify(pairs));
}
}
As StackOverflow exists, one can get pointers on how to (nearly) do that:
The proposed solution is to write one’s own collector, which works at the “business tail” of the stream only of course.
I haven’t tried this yet but one the idea arises that one might want to add a write your own collector subchapter to the book.
The problem of elegantly generating pair in the middle of the stream is still open but a reader points to a 3rd-party library called
StreamEx
:
where you can do things like
DoubleStreamEx.of(input).pairMap((a, b) -> b-a).toArray();
But I haven’t looked at that at all.
Popular Pragprog topics








Modern front-end development for Rails, second edition - Struggling to get the first chapter to work


Other popular topics










Latest in Pragprog
Latest (all)
Categories:
My Saved Portals
-
None saved yet
Popular Portals
- /elixir
- /opensuse
- /rust
- /kotlin
- /ruby
- /erlang
- /python
- /clojure
- /react
- /quarkus
- /go
- /vapor
- /v
- /react-native
- /wasm
- /security
- /django
- /nodejs
- /centos
- /haskell
- /rails
- /fable
- /gleam
- /swift
- /js
- /deno
- /assemblyscript
- /tailwind
- /laravel
- /symfony
- /phoenix
- /crystal
- /typescript
- /debian
- /adonisjs
- /julia
- /arch-linux
- /svelte
- /spring
- /c-plus-plus
- /flutter
- /preact
- /actix
- /java
- /angular
- /ocaml
- /zig
- /kubuntu
- /scala
- /zotonic
- /vim
- /rocky
- /lisp
- /html
- /keyboards
- /vuejs
- /nim
- /emacs
- /nerves
- /elm