If you really want/need a stream api for retrieving pages, you could create your own streams by implementing a Spliterator to retrieve each page in its tryAdvance() method.

It would look something like this

public class PageSpliterator implements Spliterator<Page> {
    private static final Integer PAGE_SIZE = 200;

    int offset;
    ApiService apiService;
    int selector;
    Builder builder;
    Page page;

    public PageSpliterator(ApiService apiService) {
      // initialize Builder?
    }

    @Override
    public boolean tryAdvance(Consumer<? super Page> action) {
    if (page == null || offset < page.getTotalNumEntries()) {
        Objects.requireNonNull(action);
        page = apiService.get(selector);
        action.accept(page);
        offset += PAGE_SIZE;
        selector = builder.increaseOffsetBy(PAGE_SIZE).build();
        return true;
    } else {
        // Maybe close/cleanup apiService?
        return false;
    }
    }

    @Override
    public Spliterator<Page> trySplit() {
    return null; // can't split
    }

    @Override
    public long estimateSize() {
    return Long.MAX_VALUE; // don't know in advance
    }

    @Override
    public int characteristics() {
    return IMMUTABLE; // return appropriate
    }
}

Then you could use the it like this:

StreamSupport.stream(new PageSpliterator(apiService), false)
  .flatMap(page -> page.getEntries()
  .stream())
  .forEach(item -> System.out.printf("Item with name '%s' and ID %d was found.%n", item.getName(), item.getId()));
Answer from rogerkl on Stack Overflow
Top answer
1 of 2
3

If you really want/need a stream api for retrieving pages, you could create your own streams by implementing a Spliterator to retrieve each page in its tryAdvance() method.

It would look something like this

public class PageSpliterator implements Spliterator<Page> {
    private static final Integer PAGE_SIZE = 200;

    int offset;
    ApiService apiService;
    int selector;
    Builder builder;
    Page page;

    public PageSpliterator(ApiService apiService) {
      // initialize Builder?
    }

    @Override
    public boolean tryAdvance(Consumer<? super Page> action) {
    if (page == null || offset < page.getTotalNumEntries()) {
        Objects.requireNonNull(action);
        page = apiService.get(selector);
        action.accept(page);
        offset += PAGE_SIZE;
        selector = builder.increaseOffsetBy(PAGE_SIZE).build();
        return true;
    } else {
        // Maybe close/cleanup apiService?
        return false;
    }
    }

    @Override
    public Spliterator<Page> trySplit() {
    return null; // can't split
    }

    @Override
    public long estimateSize() {
    return Long.MAX_VALUE; // don't know in advance
    }

    @Override
    public int characteristics() {
    return IMMUTABLE; // return appropriate
    }
}

Then you could use the it like this:

StreamSupport.stream(new PageSpliterator(apiService), false)
  .flatMap(page -> page.getEntries()
  .stream())
  .forEach(item -> System.out.printf("Item with name '%s' and ID %d was found.%n", item.getName(), item.getId()));
2 of 2
2

In my opinion there are not many scenarios where a do...while loop would be the best choice. This however is such a scenario.

Just because there is new stuff in Java8, does not mean you have to use it. If you still want to implement it with a foreach loop, for whatever reason, then I would go for the option you mentioned. Do the API call at the beginning and then start the foreach.

🌐
Baeldung
baeldung.com › home › java › java streams › java and infinite streams
Java and Infinite Streams | Baeldung
January 8, 2024 - Unfortunately, there is no such method on a stream and when we want to achieve functionality similar to standard do-while loop we need to use a limit() method:
Discussions

java - Convert an loop (while and for) to stream - Stack Overflow
I have started working with Java 8 and trying to convert some loops and old syntax in my code to lambdas and streams. So for example, I'm trying to convert this while and for loop to stream, but I... More on stackoverflow.com
🌐 stackoverflow.com
java - Limit a stream by a predicate - Stack Overflow
Such an operation ought to be possible with a Java 8 Stream, but it can't necessarily be done efficiently -- for example, you can't necessarily parallelize such an operation, as you have to look at elements in order. The API doesn't provide an easy way to do it, but what's probably the simplest way is to take Stream.iterator(), wrap the Iterator to have a "take-while... More on stackoverflow.com
🌐 stackoverflow.com
Transforming a while loop to a stream in Java 8 - Stack Overflow
As an exercise I'm converting some old code to functional streams. I don't know much about streams. It seems like it should be simple to convert this code, but I haven't had much luck. The method More on stackoverflow.com
🌐 stackoverflow.com
In Java, what are the advantages of streams over loops? - Stack Overflow
I was asked this at an interview and I'm not convinced I gave the best answer I could have. I mentioned that you can do a parallel search and that null values were handled by some means I couldn't More on stackoverflow.com
🌐 stackoverflow.com
🌐
Jsparrow
jsparrow.github.io › rules › enhanced-for-loop-to-stream-take-while.html
Replace For-Loop with Stream::takeWhile | jSparrow Documentation
Map<Integer, User> users = findAllSorted(); users.entrySet() .stream() .takeWhile(entry -> isEarlyCustomerId(entry.getKey())) .forEach(entry -> { User user = entry.getValue(); attachDiscount(user); });
🌐
Stackify
stackify.com › streams-guide-java-8
A Guide to Java Streams: In-Depth Tutorial With Examples
September 4, 2024 - As demonstrated, takeWhile stops once it encounters the first non-matching element, whereas filter continues through the entire stream. The dropWhile method, introduced in Java 9, acts as the complement to takeWhile. Instead of taking elements while a condition is true, dropWhile skips elements ...
Top answer
1 of 16
169

Operations takeWhile and dropWhile have been added to JDK 9. Your example code

IntStream
    .iterate(1, n -> n + 1)
    .takeWhile(n -> n < 10)
    .forEach(System.out::println);

will behave exactly as you expect it to when compiled and run under JDK 9.

JDK 9 has been released. It is available for download here: JDK 9 Releases.

2 of 16
85

Such an operation ought to be possible with a Java 8 Stream, but it can't necessarily be done efficiently -- for example, you can't necessarily parallelize such an operation, as you have to look at elements in order.

The API doesn't provide an easy way to do it, but what's probably the simplest way is to take Stream.iterator(), wrap the Iterator to have a "take-while" implementation, and then go back to a Spliterator and then a Stream. Or -- maybe -- wrap the Spliterator, though it can't really be split anymore in this implementation.

Here's an untested implementation of takeWhile on a Spliterator:

static <T> Spliterator<T> takeWhile(
    Spliterator<T> splitr, Predicate<? super T> predicate) {
  return new Spliterators.AbstractSpliterator<T>(splitr.estimateSize(), 0) {
    boolean stillGoing = true;
    @Override public boolean tryAdvance(Consumer<? super T> consumer) {
      if (stillGoing) {
        boolean hadNext = splitr.tryAdvance(elem -> {
          if (predicate.test(elem)) {
            consumer.accept(elem);
          } else {
            stillGoing = false;
          }
        });
        return hadNext && stillGoing;
      }
      return false;
    }
  };
}

static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> predicate) {
   return StreamSupport.stream(takeWhile(stream.spliterator(), predicate), false);
}
🌐
Oracle
docs.oracle.com › javase › tutorial › java › nutsandbolts › while.html
The while and do-while Statements (The Java™ Tutorials > Learning the Java Language > Language Basics)
The while statement evaluates expression, which must return a boolean value. If the expression evaluates to true, the while statement executes the statement(s) in the while block. The while statement continues testing the expression and executing its block until the expression evaluates to false.
🌐
W3Schools
w3schools.com › java › java_while_loop.asp
Java While Loop
In the next chapter, you will learn about the do while loop, which always runs the code at least once before checking the condition. ... If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail: sales@w3schools.com · If you want to report an error, or if you want to make a suggestion, send us an e-mail: help@w3schools.com · HTML Tutorial CSS Tutorial JavaScript Tutorial How To Tutorial SQL Tutorial Python Tutorial W3.CSS Tutorial Bootstrap Tutorial PHP Tutorial Java Tutorial C++ Tutorial jQuery Tutorial
Find elsewhere
🌐
W3Schools
w3schools.com › java › java_while_loop_do.asp
Java Do/While Loop
Java Examples Java Videos Java Compiler Java Exercises Java Quiz Java Code Challenges Java Server Java Syllabus Java Study Plan Java Interview Q&A Java Certificate ... The do/while loop is a variant of the while loop. This loop will execute ...
🌐
JRebel
jrebel.com › blog › java-streams-in-java-8
Using Java Streams in Java 8 and Beyond | JRebel by Perforce
Using streams in Java 8 or higher? Here's what you need to know about when to use streams and how to avoid concurrency errors. Also covered? Basic Java streams operations every Java dev should know.
🌐
Quora
quora.com › Why-did-you-choose-to-use-a-stream-instead-of-a-for-loop
Why did you choose to use a stream instead of a for-loop? - Quora
· 7y · Simply put,Streams deals with “ what” while for-loops deals with “how”. Hence Streams are more declarative compared to loop.Given the introduction of java 8 features like lambdas ,streams are more powerful compared to one liners ...
Top answer
1 of 5
421

Interesting that the interview question asks about the advantages, without asking about disadvantages, for there are are both.

Streams are a more declarative style. Or a more expressive style. It may be considered better to declare your intent in code, than to describe how it's done:

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

... says quite clearly that you're filtering matching elements from a list, whereas:

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

Says "I'm doing a loop". The purpose of the loop is buried deeper in the logic.

Streams are often terser. The same example shows this. Terser isn't always better, but if you can be terse and expressive at the same time, so much the better.

Streams have a strong affinity with functions. Java 8 introduces lambdas and functional interfaces, which opens a whole toybox of powerful techniques. Streams provide the most convenient and natural way to apply functions to sequences of objects.

Streams encourage less mutability. This is sort of related to the functional programming aspect -- the kind of programs you write using streams tend to be the kind of programs where you don't modify objects.

Streams encourage looser coupling. Your stream-handling code doesn't need to know the source of the stream, or its eventual terminating method.

Streams can succinctly express quite sophisticated behaviour. For example:

 stream.filter(myfilter).findFirst();

Might look at first glance as if it filters the whole stream, then returns the first element. But in fact findFirst() drives the whole operation, so it efficiently stops after finding one item.

Streams provide scope for future efficiency gains. Some people have benchmarked and found that single-threaded streams from in-memory Lists or arrays can be slower than the equivalent loop. This is plausible because there are more objects and overheads in play.

But streams scale. As well as Java's built-in support for parallel stream operations, there are a few libraries for distributed map-reduce using Streams as the API, because the model fits.

Disadvantages?

Performance: A for loop through an array is extremely lightweight both in terms of heap and CPU usage. If raw speed and memory thriftiness is a priority, using a stream is worse.

Familiarity.The world is full of experienced procedural programmers, from many language backgrounds, for whom loops are familiar and streams are novel. In some environments, you want to write code that's familiar to that kind of person.

Cognitive overhead. Because of its declarative nature, and increased abstraction from what's happening underneath, you may need to build a new mental model of how code relates to execution. Actually you only need to do this when things go wrong, or if you need to deeply analyse performance or subtle bugs. When it "just works", it just works.

Debuggers are improving, but even now, when you're stepping through stream code in a debugger, it can be harder work than the equivalent loop, because a simple loop is very close to the variables and code locations that a traditional debugger works with.

2 of 5
36

Syntactic fun aside, Streams are designed to work with potentially infinitely large data sets, whereas arrays, Collections, and nearly every Java SE class which implements Iterable are entirely in memory.

A disadvantage of a Stream is that filters, mappings, etc., cannot throw checked exceptions. This makes a Stream a poor choice for, say, intermediate I/O operations.

🌐
JDriven Blog
blog.jdriven.com › 2019 › 10 › loop
Java streams vs for loop - JDriven Blog
November 14, 2019 - It might take some investment in learning to do so, but this investment will pay off in the long run, both for the project and for the engineers. When googling about this issue there are 3 arguments that come up quite a bit. ... For loops work just fine / Streaming API is the fancy way of doing it.
🌐
Baeldung
baeldung.com › home › java › java streams › how to break from java stream foreach
How to Break from Java Stream forEach | Baeldung
January 25, 2024 - As Java developers, we often write code that iterates over a set of elements and performs an operation on each one. The Java 8 streams library and its forEach method allow us to write that code in a clean, declarative manner. While this is similar to loops, we are missing the equivalent of the break statement to abort iteration.
🌐
Medium
medium.com › @AlexanderObregon › javas-stream-takewhile-method-explained-121f14065a15
Java’s Stream.takeWhile() Method Explained | Medium
November 17, 2024 - Learn how Java's Stream.takeWhile() extracts elements from a stream based on a condition, stops on failure, and compares with filter() for better usage.
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › util › stream › Stream.html
Stream (Java Platform SE 8 )
2 weeks ago - If this stream contains fewer than n elements then an empty stream will be returned. This is a stateful intermediate operation. ... While skip() is generally a cheap operation on sequential stream pipelines, it can be quite expensive on ordered parallel pipelines, especially for large values of n, since skip(n) is constrained to skip not just any n elements, but the first n elements in the encounter order.
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › util › stream › package-summary.html
java.util.stream (Java Platform SE 8 )
2 weeks ago - For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy. Possibly unbounded. While collections have a finite size, streams need not.
🌐
W3Schools
w3schools.com › java › java_io_streams.asp
Java I/O Streams (Input/Output Streams)
assert abstract boolean break byte case catch char class continue default do double else enum exports extends final finally float for if implements import instanceof int interface long module native new package private protected public return requires short static super switch synchronized this throw throws transient try var void volatile while Java String Methods