ฝรั่ง: ทำไมไม่มีฟังก์ชัน Lists.filter ()


86

มีเหตุผลหรือไม่

Lists.transform()

แต่ไม่มี

Lists.filter()

เหรอ?

ฉันจะกรองรายการให้ถูกต้องได้อย่างไร? ฉันสามารถใช้

new ArrayList(Collection2.filter())

แน่นอน แต่วิธีนี้ไม่รับประกันว่าการสั่งซื้อของฉันจะยังคงเหมือนเดิมถ้าฉันเข้าใจถูกต้อง


8
FYI, List.newArrayList (Iterables.filter (... )) โดยทั่วไปเร็วกว่า ArrayList ใหม่ (Collection2.filter (... )) ตัวสร้าง ArrayList เรียกขนาด () บนคอลเลกชันที่กรองและการคำนวณขนาดต้องการให้ใช้ตัวกรองกับทุกองค์ประกอบของรายการต้นฉบับ
Jared Levy

4
@JaredLevy บางทีแทนก็ควรจะพูดว่าList.newArrayList(Iterables.filter(...)) Lists.newArrayList(Iterables.filter(...))
อับดุล

คำตอบ:


58

ไม่ได้ใช้งานเนื่องจากจะแสดงวิธีการช้าจำนวนมากที่เป็นอันตรายเช่น #get (ดัชนี) ในมุมมองรายการที่ส่งคืน (ข้อบกพร่องด้านประสิทธิภาพที่เชิญชวน) และ ListIterator ก็จะต้องใช้ความเจ็บปวดเช่นกัน (แม้ว่าฉันจะส่งแพทช์เมื่อหลายปีก่อนเพื่อปกปิดสิ่งนั้น)

เนื่องจากวิธีการจัดทำดัชนีไม่สามารถมีประสิทธิภาพในมุมมองรายการที่กรองได้จึงควรใช้วิธีการกรองที่กรองได้ซึ่งไม่มี


7
คุณสมมติว่าจะมีการส่งคืนมุมมองรายการ อย่างไรก็ตาม #filter สามารถนำไปใช้ในการส่งคืนรายการที่เป็นรูปธรรมใหม่ซึ่งจริงๆแล้วเป็นสิ่งที่ฉันคาดหวังจากวิธีการกรองสำหรับรายการซึ่งตรงข้ามกับวิธีที่สามารถใช้ซ้ำได้
Felix Leipold

@FelixLeipold นี้จะโคลนน้ำอย่างไรก็ตาม ตามที่เป็นอยู่นั้นfilterหมายถึงมุมมองอย่างสม่ำเสมอ (พร้อมกับพฤติกรรมที่บ่งบอกเป็นนัยว่า) ไม่ว่าจะเป็นIterables.filterเช่นนั้นSets.filterเป็นต้นเนื่องจากIterables.filterรวมเข้ากับสิ่งcopyOfใด ๆได้อย่างง่ายดายImmutableCollectionฉันจึงพบว่านี่เป็นการแลกเปลี่ยนการออกแบบที่ดี (เทียบกับวิธีการและชื่อพิเศษเช่นfilteredCopyหรืออะไรก็ตามสำหรับการรวมยูทิลิตี้อย่างง่าย)
Luke Usherwood

37

คุณสามารถใช้ได้Iterables.filterซึ่งจะรักษาการสั่งซื้ออย่างแน่นอน

โปรดทราบว่าด้วยการสร้างรายการใหม่คุณจะคัดลอกองค์ประกอบ (แน่นอนว่าเป็นเพียงการอ้างอิง) ดังนั้นจะไม่เป็นการดูสดในรายการเดิม การสร้างมุมมองจะค่อนข้างยุ่งยาก - พิจารณาสถานการณ์นี้:

Predicate<StringBuilder> predicate = 
    /* predicate returning whether the builder is empty */
List<StringBuilder> builders = Lists.newArrayList();
List<StringBuilder> view = Lists.filter(builders, predicate);

for (int i = 0; i < 10000; i++) {
    builders.add(new StringBuilder());
}
builders.get(8000).append("bar");

StringBuilder firstNonEmpty = view.get(0);

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

(นี่เป็นเพียงการคาดเดาในใจคุณบางทีผู้ดูแลฝรั่งคนหนึ่งอาจจะชิปด้วยเหตุผลที่แท้จริง :)


1
Collections2.filter.iteratorเพียงแค่โทรIterables.filterดังนั้นผลลัพธ์ก็เหมือนกัน
skaffman

@skaffman: ในกรณีนี้ฉันจะใช้Iterables.filterเวอร์ชันนี้เพื่อความชัดเจน
Jon Skeet

3
... เว้นแต่คุณต้องการview.size()รหัสในภายหลัง :)
Xaerxess

28

ฉันสามารถใช้ได้new List(Collection2.filter())แน่นอน แต่วิธีนี้ไม่รับประกันว่าการสั่งซื้อของฉันจะยังคงเหมือนเดิม

นี่ไม่เป็นความจริง Collections2.filter()เป็นฟังก์ชันที่มีการประเมินอย่างเกียจคร้านซึ่งจะไม่กรองคอลเล็กชันของคุณจนกว่าคุณจะเริ่มเข้าถึงเวอร์ชันที่กรองแล้ว ตัวอย่างเช่นหากคุณวนซ้ำในเวอร์ชันที่กรองแล้วองค์ประกอบที่กรองแล้วจะปรากฏออกมาจากตัววนซ้ำตามลำดับเดียวกันกับคอลเล็กชันเดิมของคุณ (ลบด้วยสิ่งที่กรองออกอย่างเห็นได้ชัด)

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

ดังนั้นหากคุณใช้เอาต์พุตCollections2.filter()เป็นอินพุตไปยังรายการใหม่คำสั่งซื้อเดิมของคุณจะยังคงอยู่

การใช้การนำเข้าแบบคงที่ (และLists.newArrayListฟังก์ชัน) จะค่อนข้างรวบรัด:

List filteredList = newArrayList(filter(originalList, predicate));

โปรดทราบว่าในขณะที่Collections2.filterจะไม่กระหายย้ำกว่าคอลเลกชันพื้นฐานLists.newArrayList จะ - ArrayListมันจะดึงทุกองค์ประกอบของคอลเลกชันกรองและคัดลอกลงในใหม่


มันเหมือน: List filteredList = newArrayList(filter(originalList, new Predicate<T>() { @Override public boolean apply(T input) { return (...); } }));หรือ ie List filteredList = newArrayList(filter(originalList, Predicates.notNull()));
Xaerxess

@Xaerxess: อ๊ะใช่ลืมคำทำนาย ... แก้ไขแล้ว
skaffman

@Bozho: ขอบคุณ ... พาฉันมานานพอแล้ว :)
skaffman

12

ดังที่จอนกล่าวไว้คุณสามารถใช้Iterables.filter(..)หรือCollections2.filter(..)และถ้าคุณไม่ต้องการมุมมองสดคุณสามารถใช้ImmutableList.copyOf(Iterables.filter(..))หรือLists.newArrayList( Iterables.filter(..))และการสั่งซื้อจะได้รับการดูแล

หากคุณสนใจในเหตุผลส่วนหนึ่งคุณสามารถไปที่https://github.com/google/guava/issues/505เพื่อดูรายละเอียดเพิ่มเติม


6

เมื่อสรุปสิ่งที่คนอื่นพูดคุณสามารถสร้าง Wrapper ทั่วไปเพื่อกรองรายการ:

public static <T> List<T> filter(Iterable<T> userLists, Predicate<T> predicate) {
    return Lists.newArrayList(Iterables.filter(userLists, predicate));
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.