เรามีปัญหาที่คล้ายกันในการแก้ไข เราต้องการใช้สตรีมที่ใหญ่กว่าหน่วยความจำระบบ (วนซ้ำผ่านวัตถุทั้งหมดในฐานข้อมูล) และสุ่มลำดับที่ดีที่สุดเท่าที่จะเป็นไปได้ - เราคิดว่าจะสามารถบัฟเฟอร์ 10,000 รายการและสุ่มได้
เป้าหมายเป็นฟังก์ชันที่รับกระแส
จากโซลูชันที่เสนอที่นี่ดูเหมือนจะมีตัวเลือกมากมาย:
- ใช้ไลบรารีเพิ่มเติมที่ไม่ใช่ java 8 ต่างๆ
- เริ่มต้นด้วยสิ่งที่ไม่ใช่สตรีมเช่นรายการเข้าถึงโดยสุ่ม
- มีสตรีมที่สามารถแยกได้อย่างง่ายดายใน Spliterator
สัญชาตญาณของเราเดิมทีจะใช้นักสะสมที่กำหนดเอง แต่นั่นหมายถึงการเลิกสตรีม โซลูชันตัวสะสมแบบกำหนดเองด้านบนนั้นดีมากและเราเกือบจะใช้แล้ว
นี่คือวิธีแก้ปัญหาที่กลโกงโดยใช้ความจริงที่ว่าStream
s สามารถให้คุณIterator
ใช้เป็นช่องทางหลบหนีเพื่อให้คุณทำอะไรพิเศษที่สตรีมไม่รองรับ Iterator
จะถูกแปลงกลับไปยังสตรีมโดยใช้บิตของ Java 8 อีก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());
}
}
}
ตัวอย่างง่ายๆของการใช้สิ่งนี้จะมีลักษณะดังนี้:
@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)
.map(list -> {
Collections.shuffle(list); return list; })
.flatMap(List::stream)
.forEach(System.out::println);
}
สิ่งนี้ให้ผลลัพธ์บางอย่างเช่น (มันสุ่มแตกต่างกันมากทุกครั้ง)
A
C
B
E
D
F
เคล็ดลับที่นี่คือมีสตรีมอยู่เสมอดังนั้นคุณสามารถใช้งานเป็นกลุ่มหรือทำบางอย่างกับแต่ละชุดแล้วflatMap
กลับไปที่สตรีม ยิ่งไปกว่านั้นทั้งหมดข้างต้นจะทำงานเป็นนิพจน์สุดท้ายforEach
หรือcollect
นิพจน์การยุติอื่น ๆ เท่านั้นดึงข้อมูลผ่านสตรีม
ปรากฎว่าiterator
เป็นการยุติการดำเนินการประเภทพิเศษบนสตรีมและไม่ทำให้สตรีมทั้งหมดทำงานและเข้ามาในหน่วยความจำ! ขอบคุณพวก Java 8 สำหรับการออกแบบที่ยอดเยี่ยม!