У нас была аналогичная проблема для решения. Мы хотели взять поток, который был больше, чем системная память (итерация по всем объектам в базе данных), и рандомизировать порядок как можно лучше - мы думали, что было бы хорошо перезагрузить 10 000 элементов и рандомизировать их.
Целью была функция, которая использовалась в потоке.
Из предложенных решений здесь, кажется, есть целый ряд вариантов:
- Используйте различные не-Java 8 дополнительных библиотек
- начать с чего-то, что это не поток - например, произвольного доступа список
- Есть поток, который может быть легко разделить на spliterator
Наш инстинкт первоначально должен был использовать пользовательский коллектор, но это означало, выпадающие из потокового видео. Вышеупомянутое решение коллектора очень хорошее, и мы его почти использовали.
Вот решение, которое чита, используя тот факт, что Stream
s может дать вам Iterator
, которые вы можете использовать в качестве аварийного люка, чтобы позволить вам сделать что-то дополнительное, что потоки не поддерживают. Iterator
преобразуется обратно в поток, используя другой бит 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());
}
}
}
Простой пример использования это будет выглядеть следующим образом:
@Test
public void getsBatches() {
BatchingIterator.batchedStreamOf(Stream.of("A","B","C","D","E","F"), 3)
.forEach(System.out::println);
}
Вышеприведенные печатает
[A, B, C]
[D, E, F]
Для нашего случая использования, мы хотели перетасовать партии, а затем сохранить их как поток - это выглядело так:
@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);
}
Это выводит что-то подобное (это рандомизированное, поэтому каждый раз разные)
A
C
B
E
D
F
Секрет соус здесь в том, что всегда есть поток, так что вы можете работать на потоке партий, или сделать что-то для каждой партии а затем flatMap
обратно в поток. Еще лучше, все вышеперечисленное работает только как окончательный forEach
или collect
или другие завершающие выражения PULL данные через поток.
Оказалось, что iterator
является специальным типом операции завершения операции по потоку и не вызывает запуск всего потока и попадания в память! Благодаря Java-парням за блестящий дизайн!
Не могли бы вы привести пример того, что вам нужно, используя цикл 'for'? – Edd
Я не могу понять, как выполнить группировку, извините, но [Files # lines] (https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html # lines-java.nio.file.Path-java.nio.charset.Charset-) будет лениво читать содержимое файла. – Toby
, так что вам в основном нужна обратная функция 'flatMap' (+ дополнительная flatMap, чтобы свернуть потоки снова)? Я не думаю, что что-то подобное существует как удобный метод в стандартной библиотеке.Либо вам придется найти стороннюю библиотеку, либо написать свой собственный на основе разделителей и/или коллектора, излучающего поток потоков. – the8472