ใน Java อะไรคือข้อดีของการสตรีมผ่านลูป? [ปิด]


133

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


เมื่อพิจารณาถึงคำตอบที่รัดกุมดูเหมือนว่านี่ไม่ใช่คำถามที่กว้างเกินไป


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

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

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

ดังที่ระบุไว้ด้านล่างคำตอบที่เพียงพอได้รับซึ่งพิสูจน์ว่ามีหนึ่งและมันเป็นเรื่องง่ายพอที่จะให้


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

19
แม้ว่ามันจะเป็น 30 ไลน์หนึ่งซับ? ฉันไม่ชอบโซ่ยาว ๆ
user447607

1
นอกจากนี้สิ่งที่ฉันกำลังมองหาที่นี่คือคำตอบที่เหมาะสมสำหรับการสัมภาษณ์ นี่เป็นเพียง "ความเห็น" ที่สำคัญ
user447607

1
การพูดอย่างมีการศึกษาคำถามนี้ช่วยให้ฉันรู้สึกผิดหวังในการสัมภาษณ์ในอนาคตด้วยเช่นกัน @slim จับมันได้จริง ๆ แต่การพูดเชิงอุตสาหกรรมมันพูดถึงว่าภาษาโปรแกรม Microsoft สร้างอาชีพของพวกเขาอย่างไรในการฉีกภาษาจาวา ปิดการแสดงออกแลมบ์ดาและลำธารจากฝ่ายตรงข้ามจะช่วยให้มองเห็นสิ่งที่ Java จะไปทำเกี่ยวกับ Structs และยูเนี่ยนในอนาคต :)
ShayHaned

5
โปรดทราบว่าสตรีมแตะเพียงส่วนหนึ่งของพลังงานในการเขียนโปรแกรมการทำงาน: - /
Thorbjørn Ravn Andersen

คำตอบ:


251

น่าสนใจที่คำถามสัมภาษณ์ถามเกี่ยวกับข้อดีโดยไม่ต้องถามถึงข้อเสียเนื่องจากมีทั้งคู่

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

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

... กล่าวอย่างชัดเจนว่าคุณกำลังกรององค์ประกอบที่ตรงกันจากรายการขณะที่:

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

บอกว่า "ฉันกำลังทำลูป" วัตถุประสงค์ของการวนซ้ำจะถูกฝังลึกลงไปในตรรกะ

ลำธารมักจะterser ตัวอย่างเดียวกันแสดงสิ่งนี้ Terser ไม่ได้ดีกว่าเสมอไป แต่ถ้าคุณสามารถพูดสั้น ๆ และแสดงออกได้ในเวลาเดียวกัน

ลำธารมีความสัมพันธ์ที่แข็งแกร่งกับฟังก์ชั่น Java 8 แนะนำ lambdas และส่วนต่อประสานการทำงานซึ่งจะเปิดกล่องของเล่นทั้งหมดของเทคนิคที่ทรงพลัง Streams ให้วิธีที่สะดวกและเป็นธรรมชาติที่สุดในการใช้ฟังก์ชั่นกับลำดับของวัตถุ

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

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

ลำธารสามารถแสดงพฤติกรรมที่ซับซ้อนได้อย่างชัดเจน ตัวอย่างเช่น:

 stream.filter(myfilter).findFirst();

อาจมองอย่างรวดเร็วก่อนราวกับว่ากรองทั้งสตรีมแล้วส่งคืนองค์ประกอบแรก แต่ในความเป็นจริงแล้วเป็นการfindFirst()ผลักดันการดำเนินการทั้งหมดดังนั้นจึงหยุดได้อย่างมีประสิทธิภาพหลังจากการค้นหาหนึ่งรายการ

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

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

ข้อเสีย?

ประสิทธิภาพการทำงาน : การforวนซ้ำผ่านอาร์เรย์มีน้ำหนักเบามากทั้งในแง่ของการใช้ฮีปและ CPU หากความเร็วที่ช้าและความจำเสื่อมเป็นสิ่งสำคัญการใช้กระแสจะแย่กว่า

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

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

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


4
ฉันคิดว่ามันยุติธรรมที่จะจัดระเบียบสิ่งต่างๆที่มีลักษณะคล้ายกระแสกลายเป็นเรื่องธรรมดามากขึ้นและตอนนี้แสดงในภาษาที่ใช้กันทั่วไปจำนวนมากซึ่งไม่ได้เน้นไปที่ FP โดยเฉพาะ
Casey

5
จากข้อดีและข้อเสียที่ระบุไว้ที่นี่ฉันคิดว่าสตรีมจะไม่คุ้มค่ากับสิ่งอื่นใดนอกจากการใช้งานที่ง่ายมาก (ตรรกะเล็กน้อยถ้า / แล้ว / อย่างอื่นไม่มีการโทรซ้อนหรือ lambdas เป็นต้น) ในส่วนประสิทธิภาพที่ไม่ใช่ critifcal
Henrik Kjus Alstad

1
@HenrikKjusAlstad นั่นไม่ใช่ของที่ฉันตั้งใจจะสื่อสาร ลำธารเป็นผู้ใหญ่ที่มีประสิทธิภาพแสดงออกและเหมาะสมอย่างสมบูรณ์สำหรับรหัสเกรดการผลิต
ผอม

โอ้ฉันไม่ได้หมายความว่าฉันจะไม่ใช้มันในการผลิต แต่ฉันจะเริ่มต้นกับ loops / ifs แบบเก่า ๆ แทนการสตรีมโดยเฉพาะถ้าสตรีมที่ได้นั้นดูซับซ้อน ฉันแน่ใจว่ามีการใช้งานที่กระแสจะเอาชนะลูปและถ้าอยู่ในความชัดเจน แต่บ่อยกว่าไม่ฉันคิดว่ามันเป็น "ผูก" หรือบางครั้งแม้แต่วิธีอื่น ๆ ดังนั้นฉันจะเพิ่มน้ำหนักในการโต้แย้งค่าโสหุ้ยการรับรู้สำหรับการเกาะติดกับวิธีการเก่า
Henrik Kjus Alstad

1
@lijepdam - แต่คุณยังคงมีรหัสที่บอกว่า "ฉันกำลังวนซ้ำรายการนี้ (ดูภายในวงเพื่อหาสาเหตุ)" เมื่อ "การวนซ้ำในรายการ" ไม่ใช่จุดประสงค์หลักของรหัส
บางเฉียบ

16

นอกจากความสนุกในเชิงไวยากรณ์แล้วสตรีมได้รับการออกแบบมาเพื่อทำงานกับชุดข้อมูลขนาดใหญ่ที่อาจเกิดขึ้นไม่ จำกัด ในขณะที่อาร์เรย์คอลเลกชันและเกือบทุกคลาส Java SE ที่ใช้ Iterable นั้นอยู่ในหน่วยความจำทั้งหมด

ข้อเสียของสตรีมคือตัวกรองการแมปและอื่น ๆ ไม่สามารถโยนข้อยกเว้นที่เลือก สิ่งนี้ทำให้ Stream เป็นตัวเลือกที่แย่สำหรับการดำเนินการพูด I / O ระดับกลาง


7
แน่นอนคุณสามารถวนลูปมากกว่าแหล่งที่ไม่มีที่สิ้นสุดเช่นกัน
บาง

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

2
@LluisMartinez ห้องสมุดฐานข้อมูลลูกค้าที่ดีจะส่งคืนสิ่งที่ชอบStream<Row>- หรือเป็นไปได้ที่จะเขียนStreamการดำเนินการของตัวเองในการตัดการดำเนินการเคอร์เซอร์ฐานข้อมูลผลลัพธ์
บาง

ลำธารนั้นไม่สนใจข้อยกเว้นอย่างเงียบ ๆ เป็นข้อบกพร่องที่ฉันถูกกัดเมื่อเร็ว ๆ นี้ unintuitive
xxfelixxx

@xxfelixxx สตรีมไม่ละเว้นข้อยกเว้นในใจ ลองใช้วิธีนี้:Arrays.asList("test", null).stream().forEach(s -> System.out.println(s.length()));
VGR

8
  1. คุณรับรู้ไม่ถูกต้อง: การดำเนินการแบบขนานใช้Streams ไม่ใช่Optionals

  2. คุณสามารถกำหนดวิธีการทำงานร่วมกับสตรีม: การใช้พวกเขาเป็นพารามิเตอร์คืนพวกเขา ฯลฯ คุณไม่สามารถกำหนดวิธีการที่ใช้วนเป็นพารามิเตอร์ วิธีนี้ช่วยให้การดำเนินการสตรีมที่ซับซ้อนหนึ่งครั้งและใช้งานได้หลายครั้ง โปรดทราบว่า Java มีข้อเสียเปรียบที่นี่: วิธีการของคุณจะต้องถูกเรียกว่าsomeMethod(stream)ตรงข้ามกับกระแสของตัวเองstream.someMethod()ดังนั้นการผสมพวกเขาทำให้การอ่านยุ่งยากขึ้น: ลองดูลำดับการดำเนินการใน

    myMethod2(myMethod(stream.transform(...)).filter(...))

    ภาษาอื่น ๆ อีกมากมาย (C #, Kotlin, Scala และอื่น ๆ ) อนุญาต "วิธีการขยาย" บางรูปแบบ

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


อธิบาย 1. อินเทอร์เฟซเสริมไม่ได้หมายถึงการจัดการค่า null ในโซ่หรือไม่ เกี่ยวกับ 3 ที่เหมาะสมเนื่องจากมีตัวกรองแบบลัดวงจรวิธีการจะถูกเรียกใช้สำหรับการเกิดขึ้นที่ระบุเท่านั้น ที่มีประสิทธิภาพ มันทำให้รู้สึกว่าฉันสามารถระบุว่าการใช้พวกเขาลดความจำเป็นในการเขียนรหัสเพิ่มเติมที่จะต้องมีการทดสอบ ฯลฯ เมื่อตรวจสอบฉันไม่แน่ใจว่าคุณหมายถึงอะไรโดยกรณีลำดับใน 2
user447607

1. Optionalเป็นทางเลือกnullแต่ไม่มีอะไรเกี่ยวข้องกับการทำงานแบบขนาน ถ้า "ตอนนี้ฉันรู้ว่าฉันกำลังคิดว่าตัวเลือก" ในคำถามของคุณเป็นเพียงการพูดคุยเกี่ยวกับการnullจัดการ?
Alexey Romanov

ฉันเปลี่ยนลำดับที่ 2 และ 3 และขยายทั้งสองเล็กน้อย
Alexey Romanov

6

คุณวนลูปมากกว่าลำดับ (อาร์เรย์, คอลเลกชัน, อินพุต, ... ) เนื่องจากคุณต้องการใช้ฟังก์ชันบางอย่างกับองค์ประกอบของลำดับ

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

ดังนั้นในกรณีส่วนใหญ่คุณสามารถแสดงมันโดยใช้รหัสน้อยลงโดยใช้ Streams นั่นคือคุณสามารถอ่านค่าได้


4
ไม่ใช่แค่อ่านง่ายเท่านั้น รหัสที่คุณไม่ต้องเขียนคือรหัสที่คุณไม่จำเป็นต้องทดสอบ
user447607

3
ดูเหมือนว่าคุณจะได้คำตอบที่ดีสำหรับการสัมภาษณ์ด้วย
wero

6

ฉันจะบอกว่าการขนานมันใช้งานง่ายมาก ลองวนซ้ำหลายล้านรายการพร้อมกับการวนซ้ำ เราไปซีพียูมากมายไม่เร็วขึ้น ดังนั้นสิ่งที่ง่ายกว่าคือการวิ่งขนานกันดีกว่าและด้วยความStreamที่นี่เป็นสายลม

สิ่งที่ฉันชอบมากก็คือความฟุ่มเฟื่อยที่พวกเขาเสนอ ต้องใช้เวลาเล็กน้อยในการทำความเข้าใจกับสิ่งที่พวกเขาทำและผลิตจริงเมื่อเทียบกับวิธีที่พวกเขาทำ

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