Finite Ströme einfach nicht über Stream.generate
erstellt.
Die Standardmethode zur Implementierung eines Streams ist die Implementierung eines Spliterator
, manchmal unter Verwendung von the Iterator
detour. In jedem Fall hat die Implementierung eine Möglichkeit, ein Ende zu melden, z. wenn Spliterator.tryAdvance
false
oder forEachRemaining
zurückgibt, kehrt die Methode zurück, oder im Fall einer Iterator
Quelle, wenn hasNext()
false
zurückgibt.
kann A Spliterator
auch die erwartete Anzahl von Elementen berichten, bevor die Verarbeitung beginnt.
Bäche, über eine der Factory-Methoden innerhalb der Stream
Schnittstelle geschaffen, wie Stream.generate
kann entweder implementiert werden, von einem Spliterator
auch oder mit internen Funktionen des Stroms Umsetzung, aber unabhängig davon, wie sie umgesetzt werden, Sie don‘ T Hände in dieser Implementierung, um ihr Verhalten zu ändern, also ist die einzige Möglichkeit, einen solchen Strom endlich zu machen, eine limit
Operation an den Strom zu ketten.
Wenn Sie einen nicht leeren endlichen Stream erstellen möchten, der nicht von einem Array oder einer Sammlung unterstützt wird und keine der vorhandenen Streamquellen passt, müssen Sie Ihre eigenen Spliterator
und create a stream out of it implementieren. Wie oben gesagt, können Sie eine vorhandene Methode verwenden, um ein Spliterator
aus einem Iterator
zu erstellen, aber Sie sollten die Versuchung widerstehen ein Iterator
nur zu benutzen, weil es vertraut ist. Ein Spliterator
ist nicht schwer zu implementieren:
/** like {@code Stream.generate}, but with an intrinsic limit */
static <T> Stream<T> generate(Supplier<T> s, long count) {
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(count, Spliterator.SIZED) {
long remaining=count;
public boolean tryAdvance(Consumer<? super T> action) {
if(remaining<=0) return false;
remaining--;
action.accept(s.get());
return true;
}
}, false);
}
Von diesem Ausgangspunkt können Sie Überschreibungen für die default
Methoden des Spliterator
Schnittstelle, Gewichtung Entwicklungsaufwand und mögliche Leistungsverbesserungen hinzuzufügen, z.B.
static <T> Stream<T> generate(Supplier<T> s, long count) {
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(count, Spliterator.SIZED) {
long remaining=count;
public boolean tryAdvance(Consumer<? super T> action) {
if(remaining<=0) return false;
remaining--;
action.accept(s.get());
return true;
}
/** May improve the performance of most non-short-circuiting operations */
@Override
public void forEachRemaining(Consumer<? super T> action) {
long toGo=remaining;
remaining=0;
for(; toGo>0; toGo--) action.accept(s.get());
}
}, false);
}
Warum vermeiden einen Iterator mit dem spliterator zu definieren? Ich habe gerade gesehen, dass BufferedReader.lines() diesen Ansatz verwendet, um zum Beispiel seinen endlichen Strom zu erzeugen. – Juru
'BufferedReader.lines()' ist ein gutes Beispiel. Schauen Sie sich die Umsetzung von 'next()' und [ 'hasNext()'] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/io /BufferedReader.java?av = f # 566) und wie sie den Zustand zwischen den Aufrufen halten müssen. Im Gegensatz dazu ist ein Spliterator einfach, es wird eine einzige Methode benötigt: 'tryAdvance (Consumer Super String> c) {String line = readLine(); if (line == null) gibt false zurück; c.accept (Linie); Rückkehr wahr; } und das ist es. Einfacher zu implementieren (Hinzufügen der Ausnahmebehandlung und immer noch die Hälfte der Code-Größe) und kein Wrapper benötigt ... – Holger
Ist diese Implementierung Thread-sicher? Muss es sein? – WillD