Fixed. Geht auch viel einfacher.
*** Edit ***
[QUOTE=cmrudolph;104499]Es ist für mich schwierig, alles auf Anhieb zu verstehen, von daher kann ich auch noch keine Verbesserungsvorschläge machen. Für mich ist das ein eher ungewohntes Feld - wenngleich es viele interessante Möglichkeiten bietet.
Was ich noch nicht verstehe ist: wie kann man einen derartigen Stream überhaupt verwenden, wenn man das Ende noch nicht kennt und der Stream rückwärts konstruiert wird? Eigentlich doch nur, indem man mindestens einmal einen tailSupplier
verwendet, oder?[/quote]
Genau, irgendwo muss das Ding lazy sein. Es hat kein Ende (solange man nicht in eine Exception oder so rennt)
Und problematisch wird es dann, wenn man einen Stream hat, der weniger als 10 Elemente besitzt und dessen toString()
-Methode aufgerufen wird…
Ein Stream besitzt immer unendlich viele Elemente (oder besser gesagt, die Fähigkeit, immer neue Elemente zu berechnen), die Frage ist, wieviele du auswerten willst.
Nehmen wir mal das:
Stream<Integer> s = Streams.unfold(Pair.of(0,1), pair -> Pair.of(Pair.of(pair.get2(), pair.get1() + pair.get2()), pair.get1()));
Das ist ein Stream der Fibonacci-Zahlen, und wenn wir BigInteger verwendet hätten, **aller **Fibonacci-Zahlen (bei der Implementierung hier gibt es natürlich einen Überlauf) - ist aber so schon hässlich genug. Wir können jetzt alles mögliche damit machen: Alle Elemente mit 42 multiplizieren, die durch 5 teilbaren herausfiltern, die 500 ersten Einträge wegschmeißen, oder die ersten 1000 Einträge in eine Liste packen. Wieviel davon **wirklich **im Speicher steht, hängt davon ab, wie weit du auf den Stream zugegriffen hast. Dabei findet jede Berechnung nur einmal statt (sonst könnten sich ja zwei Stream.head()-Aufrufe unterscheiden, etwa wenn der entsprechende Supplier System.currentTimeMillis zurückgibt).
Es wird so weit wie möglich nur das berechnet, was benötigt wird:
Stream<Integer> s = repeat(24); // 24,24,24,24,24....
Stream<Integer> t = iterate(0, x -> x + 1); //0,1,2,3,4,5....
Stream<Integer> u = Streams.zipWith(s,t, (a, b) -> a/b); //24/0, 24/1, 24/2, 24/3, 24/4 ...
System.out.println(u.tail()); // Stream(24,12,8,6,4,4,3,3,2,2...)
Stream u enthält einen potentiellen „Division by 0“-Fehler, aber das ist kein Problem bei der Konstruktion. Bei der Ausgabe würde es dann natürlich knallen, aber wenn wir mit tail() den gefährlichen Bereich einfach „wegwerfen“, passiert ebenfalls nichts.
Ich habe übrigens repeat
(und analog cycle
) mit etwas (hässlicher) Trickserei so geschrieben, dass es der Defintion
//PSEUDOCODE
public static <A> Stream<A> repeat(A a) {
Stream<A> s = Stream.<A>of(a, () -> s);
return s;
}
``` entspräche, wenn das erlaubt wäre - tail zeigt einfach wieder auf den gesamten Stream. Auch das ist nur möglich, weil die ganze Struktur lazy ist.