当前位置: 动力学知识库 > 问答 > 编程问答 >

java - Iterate through an object with self references

问题描述:

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

网友答案:

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);
网友答案:

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 + "";
        }
    }
}
分享给朋友:
您可能感兴趣的文章:
随机阅读: