우리는 해결해야 할 비슷한 문제가있었습니다. 우리는 시스템 메모리보다 큰 스트림 (데이터베이스의 모든 개체를 반복)을 가져 와서 가능한 한 최선의 순서를 무작위로 지정하려고했습니다. 10,000 개의 항목을 버퍼링하고 무작위로 지정하는 것이 좋습니다.
대상은 스트림을받는 함수였습니다.
여기에 제안 된 솔루션 중 다양한 옵션이있는 것 같습니다.
- 다양한 비 Java 8 추가 라이브러리 사용
- 스트림이 아닌 것으로 시작-예 : 임의 액세스 목록
- 분할기에서 쉽게 분할 할 수있는 스트림이 있습니다.
우리의 본능은 원래 커스텀 컬렉터를 사용하는 것이었지만 이것은 스트리밍을 중단하는 것을 의미했습니다. 위의 사용자 지정 수집기 솔루션은 매우 훌륭하며 거의 사용했습니다.
다음은 Stream
s가 탈출구Iterator
로 사용할 수 있다는 사실을 사용하여 속임수를 쓰는 솔루션입니다 . 는 자바 8의 또 다른 비트를 사용하여 스트림으로 변환 돌아 마법을.Iterator
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 8 녀석들에게 감사합니다!