เหตุใดจึงไม่ลบ TreeSet ที่มีเครื่องมือเปรียบเทียบที่กำหนดเองเอารายการที่ใหญ่กว่าออก


22

ใช้ทั้ง Java 8 และ Java 11 ให้พิจารณาสิ่งต่อไปนี้TreeSetกับตัวString::compareToIgnoreCaseเปรียบเทียบ:

final Set<String> languages = new TreeSet<>(String::compareToIgnoreCase);
languages.add("java");
languages.add("c++");
languages.add("python");

System.out.println(languages);                 // [c++, java, python]

เมื่อฉันพยายามที่จะลบองค์ประกอบที่แน่นอนที่มีอยู่ในTreeSetมันทำงาน: ทั้งหมดที่ระบุจะถูกลบออก:

languages.removeAll(Arrays.asList("PYTHON", "C++"));

System.out.println(languages);                 // [java]

แต่ถ้าฉันพยายามที่จะเอาแทนมากขึ้นกว่าที่มีอยู่ในTreeSetโทรไม่เอาอะไรเลย (นี้ไม่โทรตามมา แต่เรียกแทนของตัวอย่างด้านบน):

languages.removeAll(Arrays.asList("PYTHON", "C++", "LISP"));

System.out.println(languages);                 // [c++, java, python]

ผมทำอะไรผิดหรือเปล่า? ทำไมมันทำเช่นนี้?

แก้ไข: String::compareToIgnoreCaseเป็นตัวเปรียบเทียบที่ถูกต้อง:

(l, r) -> l.compareToIgnoreCase(r)

5
รายการข้อผิดพลาดที่เกี่ยวข้อง: bugs.openjdk.java.net/browse/JDK-8180409 (TreeSet remove พฤติกรรมที่ไม่สอดคล้องกันทั้งหมดกับ String.CASE_INSENSITIVE_ORDER)
Progman

ที่เกี่ยวข้องอย่างใกล้ชิดQ & A
Naman

ที่เกี่ยวข้อง: stackoverflow.com/questions/18123178/…
Didier L

คำตอบ:


22

นี่คือ javadoc ของremoveAll () :

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

ในการทดสอบครั้งที่สองของคุณคุณอยู่ในกรณีแรกของ javadoc ดังนั้นจึง iterates มากกว่า "จาวา", "C ++" ฯลฯ Set.of("PYTHON", "C++")และการตรวจสอบว่าพวกเขากำลังอยู่ในชุดส่งกลับโดย พวกเขาไม่ได้ดังนั้นพวกเขาจะไม่ถูกลบออก ใช้ TreeSet อื่นโดยใช้ตัวเปรียบเทียบเดียวกันเป็นอาร์กิวเมนต์และมันก็ใช้ได้ดี การใช้ชุดการใช้งานที่แตกต่างกันสองแบบการใช้งานหนึ่งequals()และอีกการใช้งานเครื่องมือเปรียบเทียบเป็นสิ่งที่อันตรายที่ต้องทำแน่นอน

ทราบว่ามีข้อผิดพลาดเปิดเกี่ยวกับเรื่องนี้: [JDK-8180409] TreeSet removeAll พฤติกรรมที่ไม่สอดคล้องกับ String.CASE_INSENSITIVE_ORDER


คุณหมายถึงว่าทั้งสองชุดจะมีคุณสมบัติเหมือนกันหรือไม่ final Set<String> subLanguages = new TreeSet<>(String::compareToIgnoreCase); subLanguages.addAll(Arrays.asList("PYTHON", "C++", "LISP")); languages.removeAll(subLanguages);
Nikolas

1
คุณอยู่ในกรณี "ถ้าชุดนี้มีองค์ประกอบน้อยลง" อธิบายโดย javadoc อีกกรณีหนึ่งคือ "ถ้าคอลเลกชันที่ระบุมีองค์ประกอบน้อยลง"
JB Nizet

8
คำตอบนี้ถูกต้อง แต่เป็นพฤติกรรมที่ไม่เข้าใจง่ายมาก TreeSetมันรู้สึกเหมือนข้อบกพร่องในการออกแบบของที่
Boann

ฉันเห็นด้วย แต่ฉันทำอะไรไม่ได้เลย
JB Nizet

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