dtonhofer

dtonhofer

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 :thinking: 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.

Where Next?

Popular Pragmatic Bookshelf topics Top

belgoros
Following the steps described in Chapter 6 of the book, I’m stuck with running the migration as described on page 84: bundle exec sequel...
New
mikecargal
Title: Hands-On Rust (Chap 8 (Adding a Heads Up Display) It looks like ​.with_simple_console_no_bg​(SCREEN_WIDTH*2, SCREEN_HEIGHT*2...
New
brunogirin
When installing Cards as an editable package, I get the following error: ERROR: File “setup.py” not found. Directory cannot be installe...
New
akraut
The markup used to display the uploaded image results in a Phoenix.LiveView.HTMLTokenizer.ParseError error. lib/pento_web/live/product_l...
New
jwandekoken
Book: Programming Phoenix LiveView, page 142 (157/378), file lib/pento_web/live/product_live/form_component.ex, in the function below: d...
New
tkhobbes
After some hassle, I was able to finally run bin/setup, now I have started the rails server but I get this error message right when I vis...
New
andreheijstek
After running /bin/setup, the first error was: The foreman' command exists in these Ruby versions: That was easy to fix: gem install fore...
New
gorkaio
root_layout: {PentoWeb.LayoutView, :root}, This results in the following following error: no “root” html template defined for PentoWeb...
New
davetron5000
Hello faithful readers! If you have tried to follow along in the book, you are asked to start up the dev environment via dx/build and ar...
New
New

Other popular topics Top

Devtalk
Hello Devtalk World! Please let us know a little about who you are and where you’re from :nerd_face:
New
AstonJ
You might be thinking we should just ask who’s not using VSCode :joy: however there are some new additions in the space that might give V...
New
Exadra37
Oh just spent so much time on this to discover now that RancherOS is in end of life but Rancher is refusing to mark the Github repo as su...
New
PragmaticBookshelf
Use WebRTC to build web applications that stream media and data in real time directly from one user to another, all in the browser. ...
New
PragmaticBookshelf
Build efficient applications that exploit the unique benefits of a pure functional language, learning from an engineer who uses Haskell t...
New
AstonJ
If you want a quick and easy way to block any website on your Mac using Little Snitch simply… File &gt; New Rule: And select Deny, O...
New
New
sir.laksmana_wenk
I’m able to do the “artistic” part of game-development; character designing/modeling, music, environment modeling, etc. However, I don’t...
New
mindriot
Ok, well here are some thoughts and opinions on some of the ergonomic keyboards I have, I guess like mini review of each that I use enoug...
New
xiji2646-netizen
There’s a GitHub repo at forrestchang/andrej-karpathy-skills that’s sitting at 97.8k stars. It’s a single CLAUDE.md file with four behavi...
New

Latest in Functional Programming in Java, Second Edition

Functional Programming in Java, Second Edition Portal ❯

Sub Categories: