Abbiamo avuto un problema simile da risolvere. Volevamo creare un flusso che fosse più grande della memoria di sistema (iterando attraverso tutti gli oggetti in un database) e rendere casuale l'ordine nel miglior modo possibile - pensavamo che sarebbe stato ok per bufferizzare 10.000 elementi e renderli casuali.
L'obiettivo era una funzione che includeva un flusso.
delle soluzioni proposte qui, sembra che ci siano una serie di opzioni:
- utilizzare vari non-Java 8 librerie aggiuntive
- iniziare con qualcosa che non è un flusso - per esempio un elenco
- accesso casuale avere un flusso che può essere diviso facilmente in spliterator
Il nostro istinto era in origine per utilizzare una collezione personalizzata, ma questo significava l'abbandono di streaming. La soluzione di raccolta personalizzata sopra è molto buona e l'abbiamo quasi utilizzata.
Ecco una soluzione che imbroglia utilizzando il fatto che Stream
s può dare un Iterator
che si può usare come una via di fuga per farti fare qualcosa in più che i flussi non supportano. Lo Iterator
viene riconvertito in uno stream utilizzando un altro frammento di stregoneria Java 8 StreamSupport
.
/**
* An iterator which returns batches of items taken from another iterator
*/
public class BatchingIterator<T> implements Iterator<List<T>> {
/**
* Given a stream, convert it to a stream of batches no greater than the
* batchSize.
* @param originalStream to convert
* @param batchSize maximum size of a batch
* @param <T> type of items in the stream
* @return a stream of batches taken sequentially from the original stream
*/
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());
}
}
}
Un semplice esempio di utilizzo di questo sarebbe simile a questa:
@Test
public void getsBatches() {
BatchingIterator.batchedStreamOf(Stream.of("A","B","C","D","E","F"), 3)
.forEach(System.out::println);
}
Le stampe di cui sopra
[A, B, C]
[D, E, F]
Per il nostro caso d'uso, abbiamo voluto mischiare le partite e poi tenerli come un flusso - assomigliava a questo:
@Test
public void howScramblingCouldBeDone() {
BatchingIterator.batchedStreamOf(Stream.of("A","B","C","D","E","F"), 3)
// the lambda in the map expression sucks a bit because Collections.shuffle acts on the list, rather than returning a shuffled one
.map(list -> {
Collections.shuffle(list); return list; })
.flatMap(List::stream)
.forEach(System.out::println);
}
Emette una cosa del genere (è randomizzato, in modo diverso ogni volta)
A
C
B
E
D
F
L'ingrediente segreto è che c'è sempre un flusso, in modo da poter sia operare su un flusso di lotti, o di fare qualcosa per ogni lotto e quindi flatMap
torna a uno stream. Ancora meglio, tutto quanto sopra funziona solo come finale forEach
o collect
o altre espressioni di terminazione PULL i dati attraverso il flusso.
Si scopre che iterator
è un tipo speciale di che termina l'operazione su uno stream e non causa l'esecuzione dell'intero stream e viene in memoria! Grazie ai ragazzi di Java 8 per un design brillante!
Potrebbe fornire un esempio di ciò che è necessario utilizzare il ciclo 'for'? – Edd
Non riesco a capire come eseguire il raggruppamento, mi spiace, ma [File # righe] (https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html # lines-java.nio.file.Path-java.nio.charset.Charset-) leggerà pigramente il contenuto del file. – Toby
quindi hai praticamente bisogno di un inverso di 'flatMap' (+ un'aggiunta flatMap per comprimere nuovamente i flussi)? Non penso che qualcosa del genere esista come metodo conveniente nella libreria standard.O dovrai trovare una lib di terze parti o scrivere la tua basata su spliterator e/o un collector che emette un flusso di stream – the8472