Çözmemiz gereken benzer bir sorun vardı. Sistem belleğinden daha büyük bir akış almak (bir veritabanındaki tüm nesneler boyunca yinelenerek) ve sıralamayı mümkün olan en iyi şekilde rastgele hale getirmek istedik - 10.000 öğeyi arabelleğe almanın ve rastgele hale getirmenin uygun olacağını düşündük.
Hedef, bir akışı ele alan bir işlevdi.
Burada önerilen çözümlerden bir dizi seçenek var gibi görünüyor:
- Java 8 olmayan çeşitli ek kitaplıkları kullanın
- Akış olmayan bir şeyle başlayın - örneğin rastgele erişim listesi
- Bir ayırıcıda kolayca bölünebilen bir akışa sahip olun
İçgüdülerimiz aslında özel bir toplayıcı kullanmaktı, ancak bu akıştan çıkmak anlamına geliyordu. Yukarıdaki özel toplayıcı çözümü çok iyi ve neredeyse kullandık.
İşte size, akışların desteklemediği fazladan bir şey yapmanıza izin vermek için bir kaçış kapısı olarak kullanabileceğiniz Stream
bir gerçeği kullanarak hile yapan bir çözüm . Java 8 başka bit kullanarak bir akışa dönüştürülmüş geri büyücülük.Iterator
Iterator
StreamSupport
public class BatchingIterator<T> implements Iterator<List<T>> {
public static <T> Stream<List<T>> batchedStreamOf(Stream<T> originalStream, int batchSize) {
return asStream(new BatchingIterator<>(originalStream.iterator(), batchSize));
}
private static <T> Stream<T> asStream(Iterator<T> iterator) {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(iterator,ORDERED),
false);
}
private int batchSize;
private List<T> currentBatch;
private Iterator<T> sourceIterator;
public BatchingIterator(Iterator<T> sourceIterator, int batchSize) {
this.batchSize = batchSize;
this.sourceIterator = sourceIterator;
}
@Override
public boolean hasNext() {
prepareNextBatch();
return currentBatch!=null && !currentBatch.isEmpty();
}
@Override
public List<T> next() {
return currentBatch;
}
private void prepareNextBatch() {
currentBatch = new ArrayList<>(batchSize);
while (sourceIterator.hasNext() && currentBatch.size() < batchSize) {
currentBatch.add(sourceIterator.next());
}
}
}
Bunu kullanmanın basit bir örneği şuna benzer:
@Test
public void getsBatches() {
BatchingIterator.batchedStreamOf(Stream.of("A","B","C","D","E","F"), 3)
.forEach(System.out::println);
}
Yukarıdaki baskılar
[A, B, C]
[D, E, F]
Kullanım durumumuz için, grupları karıştırmak ve ardından bunları bir akış olarak tutmak istedik - şuna benziyordu:
@Test
public void howScramblingCouldBeDone() {
BatchingIterator.batchedStreamOf(Stream.of("A","B","C","D","E","F"), 3)
.map(list -> {
Collections.shuffle(list); return list; })
.flatMap(List::stream)
.forEach(System.out::println);
}
Bu, şöyle bir şey çıkarır (rastgele hale getirilmiştir, her seferinde çok farklıdır)
A
C
B
E
D
F
Buradaki gizli sos, her zaman bir akarsu olması, böylece ya bir parti akışı üzerinde çalışabilir ya da her partiye bir şeyler yapıp ardından flatMap
bir akışa geri dönebilirsiniz. Daha da iyisi, yukarıda sadece tüm nihai olarak çalışır forEach
veya collect
diğer sonlandırma ifadeler PULL'U dere yoluyla veri.
Bu , bir akış üzerinde iterator
özel bir sonlandırma işlemi türü olduğu ve tüm akışın çalışıp belleğe girmesine neden olmadığı ortaya çıktı! Harika bir tasarım için Java 8 çalışanlarına teşekkürler!