3

I want to convert this java do...while() to a Java 8.

private static final Integer PAGE_SIZE = 200;
int offset = 0;

Page page = null;
do {
    // Get all items.
    page = apiService.get(selector);

    // Display items.
    if (page.getEntries() != null) {
    for (Item item : page.getEntries()) {
        System.out.printf("Item with name '%s' and ID %d was found.%n", item.getName(),
            item.getId());
    }
    } else {
    System.out.println("No items were found.");
    }

    offset += PAGE_SIZE;
    selector = builder.increaseOffsetBy(PAGE_SIZE).build();
} while (offset < page.getTotalNumEntries());

This code makes api call to apiService and retrieves data. Then, I want to loop until offset is less than totalNumberEntries.

What is prohibiting me from using while() or foreach with step or any other kind of loop loop is I don't know the totalNumberEntries without making API call (which is done inside the loop).

One option I can think of is making the API call just to get the totalNumberEntries and proceed with the loop.

Alexis C.
  • 91,686
  • 21
  • 171
  • 177
biniam
  • 8,099
  • 9
  • 49
  • 58
  • 9
    `do {...} while (...)` is perfectly legal in Java 8. – tobias_k Apr 21 '16 at 09:22
  • 1
    Please share what you have tried out – sidgate Apr 21 '16 at 09:22
  • 2
    Technically, the functional way would be a `takeWhile` that will exist in Java 9 http://stackoverflow.com/a/32304570/1743880 – Tunaki Apr 21 '16 at 09:30
  • 1
    You could be more specific what you're trying to achieve. You can always use other types of loops however, you would need to refactor your code, hence you're not prohibited from using them. If you wanted to use while() or for() loops, you would need to move the call to the 'apiService' before the loop and then repeat it inside the loop too (same for for() loop). Also, do-while is perfectly fine. – RZet Apr 21 '16 at 09:50

2 Answers2

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()));
rogerkl
  • 51
  • 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.

BT9
  • 87
  • 1
  • 11