ฉันควรส่งคืนคอลเล็กชันหรือสตรีมหรือไม่


163

สมมติว่าฉันมีวิธีที่ส่งกลับมุมมองแบบอ่านอย่างเดียวในรายการสมาชิก:

class Team {
    private List < Player > players = new ArrayList < > ();

    // ...

    public List < Player > getPlayers() {
        return Collections.unmodifiableList(players);
    }
}

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

ในสถานการณ์ทั่วไปนี้ฉันควรส่งคืนสตรีมแทนหรือไม่

public Stream < Player > getPlayers() {
    return players.stream();
}

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


12
ไม่มีอะไรผิดปกติกับสิ่งนี้ในฐานะที่เป็นสำนวน หลังจากทั้งหมดplayers.stream()เป็นเพียงวิธีการดังกล่าวซึ่งจะส่งกลับกระแสไปยังผู้โทร คำถามจริงคือคุณต้องการ จำกัด ผู้โทรไปยังการสำรวจเส้นทางเดียวและปฏิเสธการเข้าถึงคอลเลกชันของคุณผ่านCollectionAPI หรือไม่? ผู้โทรอาจต้องการไปaddAllยังคอลเล็กชันอื่นหรือไม่
Marko Topolnik

2
ทุกอย่างขึ้นอยู่กับ คุณสามารถทำ collection.stream () และ Stream.collect () ได้ตลอดเวลา ดังนั้นขึ้นอยู่กับคุณและผู้โทรที่ใช้ฟังก์ชันนั้น
ราชา Anbazhagan

คำตอบ:


222

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

ก่อนอื่นโปรดทราบว่าคุณสามารถรับคอลเล็กชันจากสตรีมได้ตลอดเวลาและในทางกลับกัน:

// If API returns Collection, convert with stream()
getFoo().stream()...

// If API returns Stream, use collect()
Collection<T> c = getFooStream().collect(toList());

ดังนั้นคำถามคือซึ่งมีประโยชน์มากขึ้นสำหรับผู้โทรของคุณ

หากผลลัพธ์ของคุณอาจไม่มีที่สิ้นสุดมีเพียงตัวเลือกเดียวเท่านั้น: สตรีม

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

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

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

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

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

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


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

8
@Marko แม้ว่าคุณจะ จำกัด คำถามของคุณอย่างแคบ ๆ ฉันก็ยังไม่เห็นด้วยกับข้อสรุปของคุณ บางทีคุณสมมติว่าการสร้างสตรีมมีราคาแพงกว่าการห่อคอลเลกชันด้วยเสื้อคลุมที่ไม่เปลี่ยนรูปแบบหรือไม่? (และแม้ว่าคุณจะทำไม่ได้มุมมองสตรีมที่คุณเห็นใน wrapper นั้นแย่กว่าที่คุณได้รับจากต้นฉบับเพราะ UnmodifiableList ไม่ได้แทนที่ตัวแยก () คุณจะสูญเสียความเท่าเทียมกันทั้งหมดได้อย่างมีประสิทธิภาพ) บรรทัดล่าง: ระวัง ของอคติที่คุ้นเคย คุณรู้จักคอลเลคชั่นมาหลายปีแล้วและนั่นอาจทำให้คุณไม่ไว้ใจผู้มาใหม่
Brian Goetz

5
@ MarkoTopolnik แน่นอน เป้าหมายของฉันคือการตอบคำถามการออกแบบ API ทั่วไปซึ่งกำลังเป็นคำถามที่พบบ่อย เกี่ยวกับค่าใช้จ่ายโปรดทราบว่าหากคุณยังไม่มีคอลเลกชันที่เกิดขึ้นคุณสามารถส่งคืนหรือตัด (OP มี แต่มักจะไม่มี) คอลเลกชันที่เป็นรูปธรรมในวิธีการทะเยอทะยานไม่ถูกกว่าการส่งคืนสตรีมและปล่อยให้ ผู้โทรเข้าปรากฏตัวขึ้นอย่างหนึ่ง (และแน่นอนว่าการทำให้เป็นจริงในช่วงต้นอาจมีราคาแพงกว่ามากถ้าผู้โทรไม่ต้องการหรือถ้าคุณส่งคืน ArrayList แต่ผู้โทรต้องการใช้ TreeSet) แต่สตรีมเป็นของใหม่และผู้คนมักจะคิดว่า มันคือ.
Brian Goetz

4
@MarkoTopolnik ในขณะที่หน่วยความจำในตัวเป็นกรณีการใช้งานที่สำคัญมากยังมีบางกรณีที่มีการสนับสนุนการขนานที่ดีเช่นลำธารที่สร้างขึ้นไม่ได้สั่งซื้อ (เช่น Stream.generate) อย่างไรก็ตามในกรณีที่สตรีมเป็นแบบที่ไม่ดีเป็นกรณีการใช้งานแบบตอบสนองซึ่งข้อมูลมาถึงด้วยเวลาแฝงแบบสุ่ม เพื่อที่ฉันจะแนะนำ RxJava
Brian Goetz

4
@MarkoTopolnik ฉันไม่คิดว่าเราไม่เห็นด้วยยกเว้นบางทีคุณอาจจะชอบเราที่จะมุ่งเน้นความพยายามของเราแตกต่างกันเล็กน้อย (เราคุ้นเคยกับสิ่งนี้ไม่สามารถทำให้ทุกคนมีความสุข) ศูนย์ออกแบบสำหรับสตรีมที่เน้นโครงสร้างข้อมูลในหน่วยความจำ ศูนย์ออกแบบสำหรับ RxJava มุ่งเน้นไปที่กิจกรรมที่สร้างจากภายนอก ทั้งคู่เป็นห้องสมุดที่ดี ทั้งคู่ก็ไม่ค่อยดีนักเมื่อคุณพยายามที่จะนำมันไปใช้กับเคสที่ออกมาจากศูนย์การออกแบบของพวกเขา แต่เพียงเพราะค้อนเป็นเครื่องมือที่น่ากลัวสำหรับเข็มที่ไม่แนะนำว่ามีอะไรผิดปกติกับค้อน
Brian Goetz

63

ฉันมีไม่กี่จุดที่จะเพิ่มคำตอบที่ดีไบรอันเก๊'

เป็นเรื่องธรรมดามากที่จะส่งคืนสตรีมจากการเรียกใช้เมธอดสไตล์ "getter" ดูหน้าใช้สตรีมใน Java 8 Javadoc และมองหา "วิธีการ ... ว่าการกลับมาสตรีม" สำหรับแพคเกจอื่น ๆ java.util.Streamกว่า วิธีการเหล่านี้มักจะอยู่ในชั้นเรียนที่เป็นตัวแทนหรือสามารถมีหลายค่าหรือการรวมตัวของบางสิ่งบางอย่าง ในกรณีเช่นนี้โดยปกติ APIs จะส่งคืนคอลเลกชันหรืออาร์เรย์ของพวกเขา ด้วยเหตุผลทั้งหมดที่ Brian กล่าวไว้ในคำตอบของเขามันมีความยืดหยุ่นในการเพิ่มวิธีการคืนค่าสตรีมที่นี่ คลาสเหล่านี้จำนวนมากมีวิธีการรวบรวมหรือการส่งคืนอาร์เรย์อยู่แล้วเนื่องจากคลาสจะต้องมาก่อน Streams API หากคุณกำลังออกแบบ API ใหม่และเหมาะสมที่จะให้วิธีการส่งคืนสตรีมอาจไม่จำเป็นต้องเพิ่มวิธีการส่งคืนคอลเลกชันด้วย

ไบรอันพูดถึงค่าใช้จ่ายในการ "ทำให้เป็นจริง" ของค่าต่างๆในคอลเล็กชัน เพื่อขยายจุดนี้มีสองค่าใช้จ่ายจริงที่นี่: ค่าใช้จ่ายของการจัดเก็บค่าในคอลเลกชัน (การจัดสรรหน่วยความจำและการคัดลอก) และค่าใช้จ่ายในการสร้างค่าในสถานที่แรก ต้นทุนหลังสามารถลดหรือหลีกเลี่ยงได้โดยการใช้ประโยชน์จากพฤติกรรมการแสวงหาความขี้เกียจของสตรีม ตัวอย่างที่ดีของสิ่งนี้คือ API ในjava.nio.file.Files:

static Stream<String>  lines(path)
static List<String>    readAllLines(path)

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

try (Stream<String> lines = Files.lines(path)) {
    List<String> firstTen = lines.limit(10).collect(toList());
}

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

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

Stream<Attribute>  attributes();
Stream<Element>    elements();

3
จุดที่ดี คุณสามารถพูดเพิ่มเติมเกี่ยวกับตำแหน่งที่คุณเห็นว่าสำนวนการตั้งชื่อนั้นเกิดขึ้นได้อย่างไรและมันมีแรงฉุดมากแค่ไหน ฉันชอบความคิดของการตั้งชื่อแบบแผนทำให้เห็นได้ชัดว่าคุณได้รับกระแสกับคอลเลกชัน - แม้ว่าฉันมักจะคาดหวังความสมบูรณ์ของ IDE ที่ "รับ" เพื่อบอกสิ่งที่ฉันจะได้รับ
Joshua Goldberg

1
ฉันยังสนใจเกี่ยวกับการตั้งชื่อสำนวน
เลือกตั้ง

5
@JoshuaGoldberg JDK ดูเหมือนว่าจะใช้สำนวนการตั้งชื่อนี้แม้ว่าจะไม่ใช่เฉพาะ พิจารณา: CharSequence.chars () และ .codePoints (), BufferedReader.lines () และ Files.lines () มีอยู่ใน Java 8 ใน Java 9 ใน Java 9 มีการเพิ่มสิ่งต่อไปนี้: Process.children (), NetworkInterface.addresses ( ), Scanner.tokens (), Matcher.results (), java.xml.catalog.Catalog.catalogs () มีการเพิ่มวิธีการส่งคืนข้อมูลอื่น ๆ ที่ไม่ได้ใช้สำนวนนี้ - Scanner.findAll () มาในใจ - แต่คำนามพหูพจน์ดูเหมือนว่าจะมีการใช้งานที่เป็นธรรมใน JDK
Stuart Marks

1

สตรีมที่ออกแบบมาให้ "ยุติ" เสมอในนิพจน์เดียวกับที่สร้างขึ้นหรือไม่

นั่นคือวิธีที่ใช้ในตัวอย่างส่วนใหญ่

หมายเหตุ: การส่งกระแสข้อมูลกลับไม่แตกต่างจากการส่งคืน Iterator (ยอมรับโดยให้พลังที่แสดงออกมากขึ้น)

IMHO ทางออกที่ดีที่สุดคือแค็ปซูลทำไมคุณทำเช่นนี้และไม่ส่งคืนคอลเลกชัน

เช่น

public int playerCount();
public Player player(int n);

หรือถ้าคุณตั้งใจจะนับ

public int countPlayersWho(Predicate<? super Player> test);

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

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

1

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

ตอนนี้บางทีนั่นอาจเป็นสัญญาณว่าคุณไม่จำเป็นต้องมีข้อยกเว้นที่ตรวจสอบซึ่งมีความยุติธรรม แต่บางครั้งก็อาจหลีกเลี่ยงไม่ได้


1

ในทางตรงกันข้ามกับคอลเลกชัน, สตรีมมีลักษณะเพิ่มเติม สตรีมที่ส่งคืนโดยเมธอดใด ๆ อาจเป็น:

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

ความแตกต่างเหล่านี้มีอยู่ในคอลเล็กชั่นด้วย แต่ก็เป็นส่วนหนึ่งของสัญญาที่ชัดเจน:

  • คอลเลกชันทั้งหมดมีขนาด Iterator / Iterable สามารถไม่มีที่สิ้นสุด
  • คอลเลกชันมีการสั่งอย่างชัดเจนหรือไม่สั่ง
  • ความเสมอภาคไม่ใช่สิ่งที่คอลเล็กชั่นให้ความสำคัญนอกเหนือไปจากความปลอดภัยของเธรด

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

  • (จำกัด , เรียงลำดับ, เรียงลำดับ)
  • (จำกัด , สั่ง, ขนาน)
  • (จำกัด , ไม่สั่ง, เรียงลำดับ) ...

วิธีการเขียนสำหรับลำธารที่ขว้าง IllegalArgumentException ถ้ากระแสข้อมูลมีลักษณะการแบ่งอัลกอริทึมของคุณเป็นเรื่องยากเนื่องจากคุณสมบัติถูกซ่อนอยู่

นั่นทำให้สตรีมเป็นเพียงตัวเลือกที่ถูกต้องในลายเซ็นเมธอดเมื่อไม่มีปัญหาใด ๆ ข้างต้นซึ่งไม่ค่อยเกิดขึ้น

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


2
ความกังวลของคุณเกี่ยวกับสตรีมไม่มีที่สิ้นสุดนั้นไม่มีมูลความจริง คำถามคือ "ฉันควรกลับคอลเลกชันหรือกระแส" หากการรวบรวมเป็นไปได้ผลที่ได้คือโดยนิยามที่แน่นอน ดังนั้นความกังวลที่ผู้โทรจะต้องทำซ้ำไม่สิ้นสุดเนื่องจากคุณสามารถส่งคืนคอลเล็กชันได้ คำแนะนำที่เหลือในคำตอบนี้ไม่ดี ฟังดูเหมือนว่าคุณจะเจอคนที่สตรีมมากเกินไปและคุณหมุนไปในทิศทางอื่น คำแนะนำที่เข้าใจได้ แต่ไม่ดี
Brian Goetz

0

ฉันคิดว่ามันขึ้นอยู่กับสถานการณ์ของคุณ อาจเป็นไปได้ว่าหากคุณTeamใช้งานIterable<Player>มันก็เพียงพอแล้ว

for (Player player : team) {
    System.out.println(player);
}

หรือในสไตล์การใช้งาน:

team.forEach(System.out::println);

แต่ถ้าคุณต้องการ API ที่สมบูรณ์และคล่องแคล่วมากขึ้นสตรีมอาจเป็นทางออกที่ดี


โปรดทราบว่าในรหัส OP ที่โพสต์จำนวนผู้เล่นนั้นเกือบจะไร้ประโยชน์นอกเหนือจากค่าประมาณ ('ผู้เล่น 1034 คนกำลังเล่นอยู่คลิกที่นี่เพื่อเริ่ม!') นี่เป็นเพราะคุณกลับมามีมุมมองที่ไม่เปลี่ยนรูปของคอลเล็กชันที่ไม่แน่นอน ดังนั้นจำนวนที่คุณได้ในขณะนี้อาจไม่เท่ากับจำนวนสามไมโครวินาทีนับจากนี้ ดังนั้นในขณะที่ส่งคืนคอลเล็กชันจะให้วิธี "ง่าย" ในการนับจำนวน (และจริงๆแล้วstream.count()ก็ค่อนข้างง่ายเช่นกัน) ตัวเลขนั้นไม่ได้มีความหมายมากนักสำหรับสิ่งอื่นนอกจากการดีบักหรือประมาณ
Brian Goetz

0

ในขณะที่ผู้ตอบแบบสอบถามที่มีรายละเอียดสูงจำนวนมากให้คำแนะนำทั่วไปที่ยอดเยี่ยม แต่ฉันประหลาดใจที่ไม่มีใครบอกได้

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


-1

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

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


7
มีเหตุผลที่อ้างถึงนี้อย่างเต็มที่? มีแหล่งที่มาหรือไม่?
Xerus

-5

ผมก็อาจจะมี 2 วิธีการหนึ่งที่จะกลับมาและเป็นหนึ่งในคอลเลกชันที่จะกลับมาเป็นCollectionStream

class Team
{
    private List<Player> players = new ArrayList<>();

// ...

    public List<Player> getPlayers()
    {
        return Collections.unmodifiableList(players);
    }

    public Stream<Player> getPlayerStream()
    {
        return players.stream();
    }

}

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

นอกจากนี้ยังเพิ่มวิธีเพิ่มอีก 1 วิธีให้กับ API ของคุณเท่านั้นดังนั้นคุณจึงไม่มีวิธีมากเกินไป


1
เพราะเขาต้องการเลือกระหว่างสองตัวเลือกและถามข้อดีข้อเสียของแต่ละข้อ ยิ่งไปกว่านั้นมันช่วยให้ทุกคนเข้าใจแนวคิดเหล่านี้ได้ดีขึ้น
Libert Piou Piou

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