กลับมาจาก lambda forEach () ใน java


103

ฉันกำลังพยายามเปลี่ยนบางส่วนสำหรับแต่ละลูปเป็นแลมบ์ดา - forEach()วิธีการเพื่อค้นหาความเป็นไปได้ของนิพจน์แลมบ์ดา สิ่งต่อไปนี้น่าจะเป็นไปได้:

ArrayList<Player> playersOfTeam = new ArrayList<Player>();      
for (Player player : players) {
    if (player.getTeam().equals(teamName)) {
        playersOfTeam.add(player);
    }
}

ด้วยแลมด้า forEach()

players.forEach(player->{if (player.getTeam().equals(teamName)) {playersOfTeam.add(player);}});

แต่อันถัดไปใช้ไม่ได้:

for (Player player : players) {
    if (player.getName().contains(name)) {
        return player;
    }
}

กับแลมด้า

players.forEach(player->{if (player.getName().contains(name)) {return player;}});

มีบางอย่างผิดปกติในไวยากรณ์ของบรรทัดสุดท้ายหรือเป็นไปไม่ได้ที่จะกลับจากforEach()วิธีการ?


ฉันยังไม่คุ้นเคยกับภายในของ lambdas แต่เมื่อฉันถามคำถามกับตัวเอง: "คุณจะกลับมาจากอะไร" ความสงสัยในตอนแรกของฉันคือมันไม่ใช่วิธีการ
Gimby

1
@Gimby ใช่returnภายในคำสั่งแลมด้ากลับมาจากแลมด้าเองไม่ใช่จากสิ่งที่เรียกว่าแลมด้า ยุติกระแสในช่วงต้น ( "ลัดวงจร") การใช้งานfindFirstตามที่ปรากฏในคำตอบเอียนโรเบิร์ต
Stuart Marks

คำตอบ:


126

returnมีการกลับมาจากการแสดงออกแลมบ์ดามากกว่าจากวิธีการที่มี แทนที่จะforEachต้องfilterสตรีม:

players.stream().filter(player -> player.getName().contains(name))
       .findFirst().orElse(null);

ที่นี่filterจำกัด สตรีมไว้เฉพาะรายการที่ตรงกับเพรดิเคตfindFirstจากนั้นส่งกลับOptionalรายการที่ตรงกันรายการแรก

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


ขอบคุณนั่นคือสิ่งที่ฉันกำลังมองหา! ดูเหมือนว่าจะมีอะไรใหม่ ๆ มากมายใน Java8 ให้สำรวจ :)
samutamm

11
สมเหตุสมผล แต่ฉันขอแนะนำว่าอย่าใช้orElse(null)กับOptionalไฟล์. ประเด็นหลักOptionalคือการระบุวิธีการระบุการมีหรือไม่มีของค่าแทนการโอเวอร์โหลด null (ซึ่งนำไปสู่ ​​NPE) หากคุณใช้optional.orElse(null)มันจะซื้อคืนปัญหาทั้งหมดที่มีค่า null ฉันจะใช้มันก็ต่อเมื่อคุณไม่สามารถแก้ไขผู้โทรได้และคาดว่าจะเป็นโมฆะจริงๆ
Stuart Marks

1
@StuartMarks แน่นอนการปรับเปลี่ยนประเภทการส่งคืนเมธอดเป็นวิธีที่Optional<Player>เป็นธรรมชาติมากขึ้นในการปรับให้เข้ากับกระบวนทัศน์ของสตรีม ฉันแค่พยายามแสดงวิธีการทำซ้ำพฤติกรรมที่มีอยู่โดยใช้ lambdas
Ian Roberts

สำหรับ (Part part: parts) ถ้า (! part.isEmpty ()) ส่งคืนเท็จ ฉันสงสัยว่าจริงๆแล้วสิ่งที่สั้นกว่า และชัดเจนขึ้น. api สตรีม java ได้ทำลายภาษา java และสภาพแวดล้อม java อย่างแท้จริง Nightmare จะทำงานในโปรเจ็กต์ Java ใด ๆ ในปี 2020
mmm

18

ฉันขอแนะนำให้คุณพยายามทำความเข้าใจ Java 8 ในภาพรวมก่อนที่สำคัญที่สุดในกรณีของคุณมันจะเป็นสตรีมแลมบ์ดาสและการอ้างอิงวิธีการ

คุณไม่ควรแปลงรหัสที่มีอยู่เป็นโค้ด Java 8 แบบทีละบรรทัดคุณควรแยกคุณสมบัติและแปลงสิ่งเหล่านั้น

สิ่งที่ฉันระบุในกรณีแรกของคุณมีดังต่อไปนี้:

  • คุณต้องการเพิ่มองค์ประกอบของโครงสร้างอินพุตลงในรายการเอาต์พุตหากตรงกับเพรดิเคตบางส่วน

มาดูกันว่าเราทำได้อย่างไรเราสามารถทำได้ดังต่อไปนี้:

List<Player> playersOfTeam = players.stream()
    .filter(player -> player.getTeam().equals(teamName))
    .collect(Collectors.toList());

สิ่งที่คุณทำที่นี่คือ:

  1. เปลี่ยนโครงสร้างอินพุตของคุณให้เป็นสตรีม (ฉันสมมติว่าที่นี่เป็นประเภทCollection<Player>ตอนนี้คุณมีไฟล์Stream<Player>.
  2. กรององค์ประกอบที่ไม่ต้องการทั้งหมดออกด้วยการPredicate<Player>แมปผู้เล่นทุกคนกับบูลีนจริงหากต้องการเก็บไว้
  3. รวบรวมองค์ประกอบที่ส่งผลให้ในรายการผ่านที่นี่เราสามารถใช้อย่างใดอย่างหนึ่งของนักสะสมห้องสมุดมาตรฐานซึ่งเป็นCollectorCollectors.toList()

นอกจากนี้ยังรวมถึงอีกสองประเด็น:

  1. รหัสกับอินเตอร์เฟซเพื่อให้รหัสกับมากกว่าList<E>ArrayList<E>
  2. ใช้การอนุมานเพชรสำหรับพารามิเตอร์ type ในnew ArrayList<>()คุณกำลังใช้ Java 8 หลังจากทั้งหมด

ตอนนี้เข้าสู่จุดที่สองของคุณ:

คุณต้องการแปลง Java ดั้งเดิมเป็น Java 8 อีกครั้งโดยไม่ต้องดูภาพรวม ส่วนนี้ได้รับคำตอบจาก@IanRobertsแล้วแม้ว่าฉันคิดว่าคุณต้องทำplayers.stream().filter(...)...ในสิ่งที่เขาแนะนำ


6

หากคุณต้องการคืนค่าบูลีนคุณสามารถใช้สิ่งนี้ได้ (เร็วกว่าตัวกรองมาก):

players.stream().anyMatch(player -> player.getName().contains(name));


1

คุณยังสามารถโยนข้อยกเว้น:

บันทึก:

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

players.stream()
       .filter(player -> player.getName().contains(name))
       .findFirst()
       .orElseThrow(MyCustomRuntimeException::new);

หากตรรกะของคุณเป็น "ข้อยกเว้นที่ขับเคลื่อน" อย่างหลวม ๆ เช่นมีที่หนึ่งในโค้ดของคุณที่ตรวจจับข้อยกเว้นทั้งหมดและตัดสินใจว่าจะทำอย่างไรต่อไป ใช้เฉพาะการพัฒนายกเว้น driven เมื่อคุณสามารถหลีกเลี่ยงเกลื่อนฐานรหัสของคุณด้วยหลายtry-catchและการขว้างปาข้อยกเว้นเหล่านี้มีการมากเป็นกรณีพิเศษที่คุณคาดว่าพวกเขาและสามารถจัดการได้อย่างถูกต้อง.)

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