How can you check when a Java 8 Stream.forEach() finishes iterating?

I want to use the parallelism that Java 8 Streams provide, but I also need certain operations to be done in a specific order or else everything will break. The problem is that using a stream means that the code is now asynchronous, and I can’t figure out how to make something happen only when it’s finished iterating over the whole set.

Right now, my code is as follows:

    public void iterateOverMap(Map<String, String> m)
    {
        AtomicInteger count = new AtomicInteger(0);
        
        m.keySet().stream().forEach((k) -> {
                    Object o = m.get(k);
                    
                    // do stuff with o
                    
                    count.incrementAndGet();
                });
        
        // spin up a new thread to check if the Stream is done
        new Thread(() -> {
            for (;;)
            {
                if (count.intValue() >= map.size())
                    break;
            }
            afterFinishedIterating();
        }).start();
    }

I don’t like the idea of having to spin up a new thread or block the main thread just to keep track of this one thing, but I can’t think of how else I could do it. Does anyone know a better alternative?

Thanks!

Answer

Stream processing is synchronous.

If you want an example of how to track the progress of a Stream, you can use the peek() intermediate operation, but please keep in mind that it should ideally be used for debugging purposes

Example taken from my other answer

Stream<MyData> myStream = readData();
final AtomicInteger loader = new AtomicInteger();
int fivePercent = elementsCount / 20;
MyResult result = myStream
    .map(row -> process(row))
    .peek(stat -> {
        if (loader.incrementAndGet() % fivePercent == 0) {
            System.out.println(loader.get() + " elements on " + elementsCount + " treated");
            System.out.println((5*(loader.get() / fivePercent)) + "%");
        }
    })
    .reduce(MyStat::aggregate);