2016-08-26 3 views
0

Предположим, у нас есть объект A, который содержит некоторые данные, относящиеся к определенному году. Этот объект также ссылается на другой объект того же класса, который содержит данные, относящиеся к предыдущему году.Итерация через объект с собственными ссылками

Возможно ли сгенерировать список всех этих объектов (текущий год, предыдущий, предыдущий год ...) с потоками Java?

С наилучшими пожеланиями

+1

Simply да , Сначала попробуйте его с помощью обычного стиля Java, а затем переведите свой алгоритм в API 'Stream'. – Flown

ответ

2

Поскольку takeWhile операция доступна в JDK 9 вы можете реализовать свой собственный Iterator<T> и превратить его в Stream<T> как:

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); 
} 

Использование может выглядеть следующим образом:

Stream<A> s = iterate(root, t -> t.next, Objects::nonNull); 
+2

Btw похоже метод «итерации» с тремя аргументами также добавляется в Java 9: ​​[см. документацию] (http://download.java.net/java/jdk9/docs/api/java/util/stream/Stream.html#iterate-T -java.util.function.Predicate-java.util.function.UnaryOperator-). –

+0

@TagirValeev Я не знал об этом методе. Спасибо за ссылку. – Flown

1

Если это абсолютно необходимо сделать с помощью потока API, вот потенциальный способ сделать это. Предполагается, что последний объект A будет иметь null в своем обращении к предыдущему году, следовательно, предикат - elem -> elem != null. Если это не null или какой-либо объект A.NULL, тогда просто измените предикат соответственно.

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 + ""; 
     } 
    } 
} 
+1

Вся реализация 'tryAdvance' может быть сокращена до' return stillGoing && splitr.tryAdvance (elem -> {if (stillGoing = predicate.test (elem)) consumer.accept (elem);}) && stillGoing; ' – Holger

Смежные вопросы