เหตุใดฉันจึงควรใช้“ การทำงานตามหน้าที่” แทนการวนซ้ำ


39
for (Canvas canvas : list) {
}

NetBeans แนะนำให้ฉันใช้ "การทำงานตามหน้าที่":

list.stream().forEach((canvas) -> {
});

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

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



15
ในตัวอย่างเฉพาะของคุณมันจะไม่เป็นที่ต้องการ
Robert Harvey

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

@RobertHarvey ไม่ได้เหรอ? ทำไมจะไม่ล่ะ?
sara

@ RobertHarvey ดีฉันยอมรับคำตอบที่ยอมรับจริงๆแสดงให้เห็นว่ารุ่นสำหรับถูกพัดออกจากน้ำสำหรับกรณีที่ซับซ้อนมากขึ้น แต่ฉันไม่เห็นว่าทำไม "ชนะ" ในกรณีเล็กน้อย คุณระบุว่ามันชัดเจนในตัวเอง แต่ฉันไม่เห็นดังนั้นฉันถาม
sara

คำตอบ:


45

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

ตัวอย่างของคุณใช้งานไม่ได้จริง ๆ พิจารณารหัสต่อไปนี้จากเว็บไซต์ของออราเคิล

List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
  if(t.getType() == Transaction.GROCERY){
    groceryTransactions.add(t);
  }
}
Collections.sort(groceryTransactions, new Comparator(){
  public int compare(Transaction t1, Transaction t2){
    return t2.getValue().compareTo(t1.getValue());
  }
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
  transactionsIds.add(t.getId());
}

สามารถเขียนได้โดยใช้สตรีม:

List<Integer> transactionsIds = 
    transactions.stream()
                .filter(t -> t.getType() == Transaction.GROCERY)
                .sorted(comparing(Transaction::getValue).reversed())
                .map(Transaction::getId)
                .collect(toList());

ตัวเลือกที่สองสามารถอ่านได้มากขึ้น ดังนั้นเมื่อคุณมีลูปซ้อนกันหรือลูปต่าง ๆ ที่ทำการประมวลผลบางส่วนมันเป็นตัวเลือกที่ดีมากสำหรับการใช้งาน Streams / Lambda API


5
มีเทคนิคการหาค่าเหมาะที่สุดเช่นmap fusion ( stream.map(f).map(g)stream.map(f.andThen(g))) build / ลดการหลอม (เมื่อสร้าง stream ในวิธีการหนึ่งแล้วส่งไปยังวิธีอื่นที่ใช้มัน, คอมไพเลอร์สามารถกำจัดกระแส) และstream fusion (ซึ่งสามารถหลอมกระแส ops จำนวนมาก ร่วมกันเป็นวงบังคับเดียว) ซึ่งสามารถทำให้การดำเนินการสตรีมมีประสิทธิภาพมากขึ้น พวกมันถูกนำไปใช้ในคอมไพเลอร์ GHC Haskell และในภาษาอื่น ๆ ของ Haskell และคอมไพเลอร์ภาษาเชิงหน้าที่อื่น ๆ และมีการใช้งานวิจัยเชิงทดลองสำหรับ Scala
Jörg W Mittag

ประสิทธิภาพอาจไม่ใช่ปัจจัยเมื่อพิจารณาจาก functional vs loop เนื่องจากคอมไพเลอร์สามารถ / ควรทำการแปลงจาก for for เป็น loop เป็นการดำเนินการถ้า Netbeans สามารถทำได้และมันถูกกำหนดให้เป็นเส้นทางที่ดีที่สุด
Ryan

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

@BokMcDonagh ประสบการณ์ของฉันคือมันไม่สามารถอ่านได้สำหรับนักพัฒนาที่ไม่ได้คุ้นเคยกับ abstractions ใหม่ ฉันขอแนะนำให้ใช้ API ดังกล่าวให้มากขึ้นเพื่อทำความคุ้นเคยมากขึ้นเพราะเป็นอนาคต ไม่เพียง แต่ในโลก Java
luboskrnac

16

ข้อดีอีกประการของการใช้ API การทำงานแบบสตรีมคือการซ่อนรายละเอียดการใช้งาน มันอธิบายเฉพาะสิ่งที่ควรทำไม่ใช่วิธี ข้อได้เปรียบนี้จะชัดเจนเมื่อดูการเปลี่ยนแปลงที่ต้องทำเพื่อเปลี่ยนจากการประมวลผลแบบเธรดเดี่ยวเป็นแบบขนาน เพียงแค่เปลี่ยนไป.stream().parallelStream()


13

ถ้ามีอะไรมันยากที่จะอ่านและเข้าใจ

นั่นเป็นเรื่องส่วนตัว ฉันพบว่ารุ่นที่สองง่ายต่อการอ่านและทำความเข้าใจ มันตรงกับวิธีการภาษาอื่น ๆ (เช่น Ruby, Smalltalk, Clojure, Io, Ioke, Seph) ทำมันต้องใช้แนวคิดน้อยลงที่จะเข้าใจ (เป็นเพียงวิธีการปกติเรียกเหมือนคนอื่น ๆ ในขณะที่ตัวอย่างแรกคือไวยากรณ์เฉพาะ)

ถ้ามีอะไรมันเป็นเรื่องของความคุ้นเคย


6
ใช่นั่นเป็นเรื่องจริง. แต่นี่ดูเหมือนความคิดเห็นมากกว่าคำตอบสำหรับฉัน
โอเมก้า

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