Do-while with Java8-Optional

I often use the do-while-checkNextForNull-getNext loop template (I donโ€™t know if there is an official name for it) in some of my projects. But in Java8, using an option is considered cleaner code than checking for null references in client code. But when you use the options in this loop pattern, the code becomes a bit verbose and ugly, but since the option has several convenient methods, I would expect a cleaner way than the one I came up with below.

Example:

Given the following class.

class Item { int nr; Item(nr) { this.nr = nr; // an expensive operation } Item next() { return ...someCondition.... ? new Item(nr + 1) : null; } } 

The first element always has nr == 1, and each element defines the next element, and you do not want to create unnecessary new elements.

In client code, I can use the following do-while-checkNextForNull-getNext loop:

 Item item = new Item(1); do { // do something with the item .... } while ((item = item.next()) != null); 

With Java8 optional, this class will look like this:

 class Item { .... Optional<Item> next() { return ...someCondition.... ? Optional.of(new Item(nr + 1)) : Optional.empty(); } } 

And then the while-checkNextForNull-getNext loop pattern gets a little ugly and verbose:

 Item item = new Item(1); do { // do something with the item .... } while ((item = item.next().orElse(null)) != null); 

The orElse(null)) != null feels uncomfortable.

I searched for other types of loops but couldn't find a better one. Is there a cleaner solution?

Update:

You can use the for-each loop while avoiding null references (using null references is considered bad practice). This solution was proposed by Xavier Delamotte and does not need Java8-optional.

Implementation with a common iterator:

 public class Item implements Iterable<Item>, Iterator<Item> { int nr; Item(int nr) { this.nr = nr; // an expensive operation } public Item next() { return new Item(nr + 1); } public boolean hasNext() { return ....someCondition.....; } @Override public Iterator<Item> iterator() { return new CustomIterator(this); } } 

and

 class CustomIterator<T extends Iterator<T>> implements Iterator<T> { T currentItem; boolean nextCalled; public CustomIterator(T firstItem) { this.currentItem = firstItem; } @Override public boolean hasNext() { return currentItem.hasNext(); } @Override public T next() { if (! nextCalled) { nextCalled = true; return currentItem; } else { currentItem = currentItem.next(); return currentItem; } } } 

Then the client code becomes very simple / clean:

 for (Item item : new Item(1)) { // do something with the item .... } 

Although this can be considered a violation of the Iterator contract, because the new Item(1) object is included in the loop, whereas usually the for loop immediately calls next () and thereby skips the first object. In other words: for the first object, next () is violated because it returns the first object.

+8
java loops java-8 while-loop optional
source share
4 answers

You can do something like this:

 Optional<Item> item = Optional.of(new Item(1)); do { Item value = item.get(); // do something with the value .... } while ((item = value.next()).isPresent()); 

or (to avoid an extra variable):

 Optional<Item> item = Optional.of(new Item(1)); do { // do something with item.get() .... } while ((item = item.get().next()).isPresent()); 
+7
source share

in Java8, using an option is considered cleaner code than checking for null references in client code

No, it's the other way around: you can optionally use it where it helps you write cleaner code. Where it is not, just stick to the old idiom. Do not feel any pressure to use it if your existing idiom looks beautiful, and that, in my opinion. As an example, this would be a good use of the option:

 item.next().map(Object::toString).ifPresent(System.out::println); 

Since you need to break out of the loop at the first optional Optional, this will not help.

However, I assume that your true interest is more general: using Java 8 features for your code. The abstraction you have to choose is the stream:

 itemStream(() -> new Item(1)).forEach(item -> { ... all you need ... }); 

And, of course, now you can start working with stream processing:

 itemStream(() -> new Item(1)).filter(item.nr > 3).mapToInt(Item::nr).sum(); 

This is how you would build a stream:

 import java.util.Spliterators; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class ItemSpliterator extends Spliterators.AbstractSpliterator<Item> { private Supplier<Item> supplyFirst; private Item lastItem; public ItemSpliterator(Supplier<Item> supplyFirst) { super(Long.MAX_VALUE, ORDERED | NONNULL); this.supplyFirst = supplyFirst; } @Override public boolean tryAdvance(Consumer<? super Item> action) { Item item; if ((item = lastItem) != null) item = lastItem = item.next(); else if (supplyFirst != null) { item = lastItem = supplyFirst.get(); supplyFirst = null; } else return false; if (item != null) { action.accept(item); return true; } return false; } public static Stream<Item> itemStream(Supplier<Item> supplyFirst) { return StreamSupport.stream(new ItemSpliterator(supplyFirst), false); } } 

With this, you are a small step away from the ability to seamlessly parallelize your calculations. Since the flow of goods is mostly consistent, I suggest looking at the blog post on this subject.

+6
source share

Just add loop support to your API:

 class Item { int nr; Item(int nr) { this.nr = nr; // an expensive operation } public void forEach(Consumer<Item> action) { for(Item i=this; ; i=new Item(i.nr + 1)) { action.accept(i); if(!someCondition) break; } } public Optional<Item> next() { return someCondition? Optional.of(new Item(nr+1)): Optional.empty(); } } 

Then you can just iterate over the lambda expression

  i.forEach(item -> {whatever you want to do with the item}); 

or method links

  i.forEach(System.out::println); 

If you want to support more complex operations than just forEach loops, thread support is the right way. Its similarity is that your implementation encapsulates how to iterate over Item s.

+3
source share

Since this is due to some kind of design, I came up with the design below.

Create an interface that supports the next option.

 public interface NextProvidble<T> { Optional<T> next(); } 

The element implements the NextProvidble interface.

 public class Item implements NextProvidble<Item> { int nr; Item(int nr) { this.nr = nr; // an expensive operation } @Override public Optional<Item> next() { return /*...someCondition....*/ nr < 10 ? Optional.of(new Item(nr + 1)) : Optional.empty(); } @Override public String toString() { return "NR : " + nr; } } 

Here I use / ... someCondition .... / as nr <10

And a new class for Custom Do While, as shown below.

 public abstract class CustomDoWhile<T extends NextProvidble<T>> { public void operate(T t) { doOperation(t); Optional<T> next = t.next(); next.ifPresent( nextT -> operate(nextT)); } protected abstract void doOperation(T t); } 

Now, what should you do in your client code.

  new CustomDoWhile<Item>() { @Override protected void doOperation(Item item) { System.out.println(item.toString()); } }.operate(new Item(1)); 

It can be very clear. Please add your thoughts.

0
source share

All Articles