จะมั่นใจลำดับการประมวลผลในสตรีม java8 ได้อย่างไร


148

ฉันต้องการประมวลผลรายการภายในXMLวัตถุ java ฉันต้องแน่ใจว่าประมวลผลองค์ประกอบทั้งหมดตามลำดับที่ได้รับ

ฉันควรโทรหาsequentialแต่ละครั้งที่streamฉันใช้หรือไม่ list.stream().sequential().filter().forEach()

หรือมันก็เพียงพอแล้วที่จะใช้กระแสตราบเท่าที่ฉันไม่ได้ใช้คู่ขนาน? list.stream().filter().forEach()

คำตอบ:


339

คุณกำลังถามคำถามผิด คุณจะถามเกี่ยวsequentialกับparallelในขณะที่คุณต้องการไปยังรายการที่กระบวนการในการสั่งซื้อเพื่อให้คุณมีจะถามเกี่ยวกับการสั่งซื้อ หากคุณมีสตรีมที่สั่งซื้อและดำเนินการซึ่งรับประกันว่าจะรักษาลำดับนั้นไม่สำคัญว่าสตรีมจะถูกประมวลผลแบบขนานหรือเรียงตามลำดับ การดำเนินการจะรักษาลำดับ

คุณสมบัติที่สั่งซื้อนั้นแตกต่างจากการเปรียบเทียบแบบขนานกับการเรียงตามลำดับ เช่นถ้าคุณโทรstream()ในHashSetกระแสจะเรียงลำดับในขณะที่โทรstream()บนListผลตอบแทนกระแสรับคำสั่ง โปรดทราบว่าคุณสามารถโทรติดต่อunordered()เพื่อปล่อยสัญญาการสั่งซื้อและอาจเพิ่มประสิทธิภาพ เมื่อสตรีมไม่มีคำสั่งซื้อจะไม่มีวิธีในการสร้างคำสั่งซื้ออีกครั้ง (วิธีเดียวที่จะเปลี่ยนการสตรีมที่ไม่ได้เรียงตามลำดับให้เป็นการสั่งซื้อคือการโทรsortedอย่างไรก็ตามผลลัพธ์ที่ได้นั้นไม่จำเป็นต้องเป็นคำสั่งดั้งเดิม)

เห็นแล้วยังส่วน“สั่งซื้อ”ของเอกสารแพคเกจjava.util.stream

เพื่อให้แน่ใจว่าการบำรุงรักษาของการสั่งซื้อตลอดทั้งการดำเนินการสตรีมคุณต้องศึกษาเอกสารของแหล่งที่มาของการดำเนินงานกลางทั้งหมดและการดำเนินงานสถานีไม่ว่าพวกเขาจะรักษาคำสั่งซื้อหรือไม่ (หรือแหล่งที่มามีคำสั่งใน สถานที่).

สิ่งนี้อาจบอบบางมากเช่นStream.iterate(T,UnaryOperator)สร้างสตรีมที่สั่งซื้อในขณะที่Stream.generate(Supplier)สร้างสตรีมที่ไม่เรียงลำดับ โปรดทราบว่าคุณทำผิดพลาดโดยทั่วไปในคำถามของคุณเช่นเดียวกับที่ไม่รักษาลำดับ คุณต้องใช้หากคุณต้องการประมวลผลองค์ประกอบของสตรีมในลำดับที่รับประกันforEach forEachOrdered

ดังนั้นหากlistคำถามของคุณเป็นจริงวิธีการjava.util.Listของมันstream()จะส่งคืนสตรีมที่สั่งซื้อและfilterจะไม่เปลี่ยนลำดับ ดังนั้นหากคุณเรียกใช้list.stream().filter() .forEachOrdered()องค์ประกอบทั้งหมดจะถูกประมวลผลตามลำดับในขณะlist.parallelStream().filter().forEachOrdered()ที่องค์ประกอบอาจถูกประมวลผลแบบขนาน (เช่นโดยตัวกรอง) แต่การกระทำของเทอร์มินัลจะยังคงถูกเรียกใช้ตามลำดับ (ซึ่งเห็นได้ชัดว่าจะลด .

ตัวอย่างเช่นหากคุณใช้การดำเนินการเช่น

List<…> result=inputList.parallelStream().map(…).filter(…).collect(Collectors.toList());

การดำเนินการทั้งหมดอาจได้รับประโยชน์จากการดำเนินการแบบขนาน แต่รายการผลลัพธ์จะอยู่ในลำดับที่ถูกต้องเสมอไม่ว่าคุณจะใช้สตรีมแบบขนานหรือแบบต่อเนื่องก็ตาม


48
ใช่คำตอบที่ดี สิ่งหนึ่งที่ฉันพบคือศัพท์ที่เราใช้อย่างน้อยก็ในภาษาอังกฤษเช่น "ก่อนหน้า" "หลัง" และอื่น ๆ ค่อนข้างคลุมเครือ การสั่งซื้อมีสองแบบที่นี่: 1) การเผชิญหน้า (หรือที่รู้จักกันในชื่อการจัดลำดับเชิงพื้นที่ ) และ 2) ลำดับการประมวลผล (หรือที่เรียกว่าคำสั่งชั่วคราว ) ด้วยความแตกต่างในใจมันอาจจะเป็นประโยชน์ในการใช้คำเช่น "left of" หรือ "right of" เมื่อพูดถึงลำดับการเผชิญหน้าและ "เร็วกว่า" หรือ "ช้ากว่า" เมื่อพูดถึงลำดับการประมวลผล
Stuart Marks

ฉันเข้าใจว่าList<>จะรักษาระเบียบ แต่จะCollection<>?
Josh C.

5
@JoshC ขึ้นอยู่กับประเภทของคอลเลกชันจริง Setมักจะทำไม่ได้เว้นแต่จะเป็นหรือSortedSet LinkedHashSetมุมมองของคอลเลกชันMap( keySet(), entrySet()และvalues()) สืบทอดMapนโยบายคือเมื่อมีการสั่งซื้อแผนที่เป็นหรือSortedMap LinkedHashMapพฤติกรรมจะถูกกำหนดโดยลักษณะที่รายงานโดยspliterator คอลเลกชัน การdefaultใช้งานของCollectionไม่ได้รายงานORDEREDลักษณะดังนั้นจึงไม่ได้เรียงลำดับเว้นแต่จะถูกแทนที่
Holger

@ โฮลเกอร์ฉันมีคำถามที่อาจเกี่ยวข้องกับคำตอบของคุณเล็กน้อย
Naman

1
ไม่น่าสังเกตว่าforEachOrderedจะแตกต่างกันforEachเมื่อใช้ลำธารแบบขนาน - แต่วิธีปฏิบัติที่ดีที่จะใช้ต่อไปเมื่อสั่งเรื่องในกรณีที่วิธีการนึ่งเปลี่ยนแปลงตลอดเวลา ...
Steve Chambers

0

โดยสังเขป:

การสั่งซื้อขึ้นอยู่กับโครงสร้างข้อมูลต้นทางและการดำเนินการสตรีมระดับกลาง สมมติว่าคุณกำลังใช้Listการประมวลผลควรสั่งซื้อ (เนื่องจากfilterจะไม่เปลี่ยนลำดับที่นี่)

รายละเอียดเพิ่มเติม:

Sequential vs Parallel vs Unordered:

Javadocs

S sequential()
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
This is an intermediate operation.
S parallel()
Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.
This is an intermediate operation.
S unordered()
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
This is an intermediate operation.

สตรีมสั่งซื้อ:

Javadocs

ลำธารอาจมีหรือไม่มีคำสั่งเผชิญหน้า การสตรีมมีลำดับการเผชิญหน้าหรือไม่นั้นขึ้นอยู่กับแหล่งที่มาและการดำเนินการระดับกลาง แหล่งที่มาของกระแสข้อมูลบางอย่าง (เช่นรายการหรืออาร์เรย์) มีการเรียงลำดับจากภายในขณะที่แหล่งอื่น (เช่น HashSet) ไม่ได้รับการจัดเตรียม การดำเนินการระดับกลางบางอย่างเช่น sort () อาจกำหนดลำดับการพบบนสตรีมที่ไม่เรียงลำดับและอื่น ๆ อาจแสดงการเรียงลำดับสตรีมแบบไม่เรียงลำดับเช่น BaseStream.unordered () นอกจากนี้การทำงานของเทอร์มินัลบางอย่างอาจเพิกเฉยต่อคำสั่งเผชิญหน้าเช่น forEach ()

หากมีการสั่งซื้อกระแสการดำเนินงานส่วนใหญ่จะถูก จำกัด ให้ใช้งานกับองค์ประกอบตามลำดับการเผชิญหน้า หากที่มาของกระแสข้อมูลเป็นรายการที่มี [1, 2, 3] ดังนั้นผลลัพธ์ของการดำเนินการแผนที่ (x -> x * 2) จะต้องเป็น [2, 4, 6] อย่างไรก็ตามหากแหล่งที่มาไม่มีคำสั่งพบการกำหนดแล้วการเปลี่ยนแปลงใด ๆ ของค่า [2, 4, 6] จะเป็นผลที่ถูกต้อง

สำหรับสตรีมแบบต่อเนื่องการมีหรือไม่มีคำสั่งพบจะไม่ส่งผลต่อประสิทธิภาพ หากมีการสั่งซื้อกระแสการดำเนินการของท่อส่งกระแสข้อมูลที่เหมือนกันซ้ำ ๆ บนแหล่งเดียวกันจะให้ผลลัพธ์ที่เหมือนกัน หากไม่ได้รับคำสั่งการดำเนินการซ้ำ ๆ อาจให้ผลลัพธ์ที่แตกต่าง

สำหรับสตรีมแบบขนานการผ่อนคลายข้อ จำกัด ในการสั่งซื้อในบางครั้งอาจทำให้การดำเนินการมีประสิทธิภาพมากขึ้น การดำเนินการรวมบางอย่างเช่นการกรองรายการที่ซ้ำกัน (ชัดเจน ()) หรือการลดการจัดกลุ่ม (Collector.groupingBy ()) สามารถดำเนินการได้อย่างมีประสิทธิภาพมากขึ้นหากการสั่งซื้อองค์ประกอบไม่เกี่ยวข้อง ในทำนองเดียวกันการดำเนินการที่เชื่อมโยงกับภายในเพื่อเผชิญกับการสั่งซื้อเช่น limit () อาจต้องมีการกำหนดบัฟเฟอร์เพื่อให้แน่ใจว่าการสั่งซื้อที่เหมาะสมโดยไม่ทำลายประโยชน์ของการขนาน ในกรณีที่กระแสมีคำสั่งพบ แต่ผู้ใช้ไม่สนใจโดยเฉพาะอย่างยิ่งเกี่ยวกับคำสั่งพบว่าการยกเลิกการสั่งซื้อกระแสกับ unordered () อย่างชัดเจนอาจปรับปรุงประสิทธิภาพการทำงานแบบขนานสำหรับการดำเนินงาน stateful หรือ terminal บางอย่าง อย่างไรก็ตามท่อส่งกระแสส่วนใหญ่เช่นตัวอย่าง "ผลรวมของน้ำหนักของบล็อก" ด้านบน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.