0

Let's assume we have an object A that holds some data appertaining to a certain year. This object has also a reference to another object of the same class which holds data appertaining to the previous year.

Is it possible to generate a list of all these objects (current year, previos, previos last year...) with the Java streams?

Best regards

smsnheck
  • 1,563
  • 3
  • 21
  • 33
  • 1
    Simply yes. First try it with the common Java style then transfrom your algorithm to `Stream` API. – Flown Aug 26 '16 at 20:09

2 Answers2

2

Since takeWhile operation is available in JDK 9 you can implement your own Iterator<T> and transform it into a Stream<T> like:

private static <T> Stream<T> iterate(T root, UnaryOperator<T> generator, Predicate<T> stop) {
  return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<T>() {
    private T t = root;

    @Override
    public boolean hasNext() {
      return stop.test(t);
    }

    @Override
    public T next() {
      T result = t;
      t = generator.apply(t);
      return result;
    }
  }, Spliterator.IMMUTABLE | Spliterator.ORDERED), false);
}

The usage might look like:

Stream<A> s = iterate(root, t -> t.next, Objects::nonNull);
Flown
  • 11,480
  • 3
  • 45
  • 62
  • 2
    Btw similar three-arg `iterate` method is also added to Java 9: [see documentation](http://download.java.net/java/jdk9/docs/api/java/util/stream/Stream.html#iterate-T-java.util.function.Predicate-java.util.function.UnaryOperator-). – Tagir Valeev Aug 28 '16 at 18:49
  • @TagirValeev I wasn't aware of this method. Thanks for the reference. – Flown Aug 28 '16 at 20:06
1

If it absolutely has to be done using the Stream API, here's a potential way of doing it. It assumes that the very last object A will have null in its reference to previous year, hence the predicate - elem -> elem != null. If it's not null, or some kind of A.NULL object then simply modify the predicate accordingly.

import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class Test {

    public static void main(String[] args) {
        A a = new A(2016);
        a.prev = new A(2015);
        a.prev.prev = new A(2014);
        a.prev.prev.prev = new A(2013);

        // .. etc

        List<A> list = takeWhile(Stream.iterate(a, elem -> elem.prev),
                elem -> elem != null)
                .collect(Collectors.toList());

        // this prints - 2016, 2015, 2014, 2013
        System.out.println(list);
    }

    /**
     * This has been taken from this SO answer:
     * http://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate
     */
    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);
    }

    static class A {

        A prev;
        int year;
        // some other data

        public A(int year) {
            this.year = year;
        }

        @Override
        public String toString() {
            return year + "";
        }
    }
}
AlmasB
  • 3,377
  • 2
  • 15
  • 17
  • 1
    The entire `tryAdvance` implementation could be shortened to `return stillGoing && splitr.tryAdvance(elem -> { if(stillGoing=predicate.test(elem)) consumer.accept(elem); }) && stillGoing;` – Holger Aug 29 '16 at 09:50