JavaRush /จาวาบล็อก /Random-TH /คำถาม 10 อันดับแรกเกี่ยวกับคอลเลกชันใน Java
FedoraLinux
ระดับ
Москва

คำถาม 10 อันดับแรกเกี่ยวกับคอลเลกชันใน Java

เผยแพร่ในกลุ่ม
บทความ นี้ เป็นการแปลบทความ" คำถาม 10 อันดับแรกเกี่ยวกับ Java Collections " ด้านล่างนี้เป็นคำถามยอดนิยมเกี่ยวกับคอลเลกชันใน Java ที่ถูกถามและพูดคุยใน Stackowerflow ก่อนที่คุณจะดูคำถามเหล่านี้ คุณควรดูแผนภาพลำดับชั้นของชั้นเรียนก่อน 1. เมื่อใดจึงควรใช้ LinkedList แทน ArrayList ArrayListจริงๆ แล้วคืออาร์เรย์ องค์ประกอบต่างๆ ของอาร์เรย์สามารถเข้าถึงได้โดยตรงจากดัชนี หากอาร์เรย์ล้น จำเป็นต้องสร้างอาร์เรย์ใหม่ที่มีพื้นที่มากขึ้น การวางและย้ายองค์ประกอบทั้งหมดจะใช้เวลา O(n) นอกจากนี้ การเพิ่มและการลบองค์ประกอบยังเป็นสิ่งจำเป็นเพื่อย้ายองค์ประกอบที่มีอยู่ในอาร์เรย์ นี่อาจเป็นความไม่สะดวกที่ใหญ่ที่สุดในการใช้ ArrayList LinkedList เป็นรายการลิงก์องค์ประกอบคู่ ดังนั้น ในการเข้าถึงองค์ประกอบที่อยู่ตรงกลาง คุณต้องค้นหาตั้งแต่ต้นจนจบชีต ในทางกลับกัน การเพิ่มและการลบองค์ประกอบใน LinkedList จะเร็วกว่า เนื่องจากการดำเนินการเหล่านี้เปลี่ยนแปลงเฉพาะรายการเท่านั้น เวลาที่เลวร้ายที่สุดจะถูกเปรียบเทียบด้านล่าง:
วิธี รายการอาร์เรย์ รายการที่เชื่อมโยง
รับ (ดัชนี) โอ(1) บน)
เพิ่ม(อี) บน) โอ(1)
เพิ่ม (E, ดัชนี) บน) บน)
ลบ (ดัชนี) บน) บน)
ตัววนซ้ำ.remove() บน) โอ(1)
Iterator.เพิ่ม(E) บน) โอ(1)
แม้จะมีเวลาทำงาน แต่การใช้หน่วยความจำต้องได้รับการพิจารณาแยกกันสำหรับรายการขนาดใหญ่ ใน LinkedList แต่ละโหนดต้องมีตัวชี้เพิ่มเติมอย่างน้อยสองตัวเพื่อเชื่อมโยงโหนดก่อนหน้าและโหนดถัดไป ในขณะที่อยู่ใน ArrayList จำเป็นต้องมีเพียงอาร์เรย์ขององค์ประกอบเท่านั้น การ เปรียบเทียบเพิ่มเติมของ รายการ ArrayList, LinkedList และ Vector 2. มีประสิทธิภาพเทียบเท่ากับการลบองค์ประกอบระหว่างการวนซ้ำของคอลเลก ชัน วิธีเดียวที่ถูกต้องในการแก้ไข (ลบองค์ประกอบ) คอลเลกชันระหว่างการวนซ้ำคือการใช้ Iterator.remove( ) ตัวอย่างเช่น: ข้อผิดพลาดที่พบบ่อยที่สุดคือ: คุณจะได้รับ ConcurrentModificationExceptionขณะเรียกใช้โค้ดด้านบน สิ่งนี้เกิดขึ้นเนื่องจากตัววนซ้ำถูกสร้างขึ้นเพื่อเลื่อนไปตามรายการทั้งหมด แต่ในขณะเดียวกัน แผ่นงานก็ถูกเปลี่ยนโดยการเรียก Iterator.remove() ตามที่เขียนไว้ในเอกสารประกอบสำหรับข้อยกเว้นนี้ Iterator itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); } for(Integer i: list) { list.remove(i); }
"โดยทั่วไปไม่อนุญาตให้เธรดหนึ่งแก้ไขคอลเลกชันในขณะที่เธรดอื่นกำลังวนซ้ำอยู่"
โดยทั่วไป เป็นที่ยอมรับไม่ได้สำหรับเธรดหนึ่งที่จะแก้ไขคอลเลกชั่นในขณะที่เธรดอื่นกำลังข้ามผ่านคอลเลกชั่นนั้น 3. จะแปลง List เป็นอาร์เรย์ int[] ได้อย่างไร? วิธีที่ง่ายที่สุดในการทำเช่น นี้ คือการใช้ ArrayUtilsซึ่งอยู่ใน ไลบรารี Apache Commons Lang int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0])); ไม่มีทางลัดสำหรับนิพจน์นี้ใน JDK โปรดจำไว้ว่าคุณไม่สามารถใช้ List.toArray() ได้เนื่องจากนิพจน์นี้จะแปลง List เป็น Integer[] (ซึ่ง ไม่ใช่ประเภทดั้งเดิม) วิธีที่ถูกต้องคือตัวเลือกต่อไปนี้: int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); } 4. จะแปลงอาร์เรย์ int[] เป็นรายการได้อย่างไร? วิธีที่ง่ายที่สุดคือการใช้ ArrayUtilsใน ไลบรารี Apache Commons Langดังที่กล่าวข้างต้น List list = Arrays.asList(ArrayUtils.toObject(array)); นอกจากนี้ ไม่มีทางลัดสำหรับนิพจน์นี้ใน JDK 5. วิธีที่ดีที่สุดในการกรองคอลเลกชันคืออะไร? คุณสามารถใช้แพ็คเกจของบริษัทอื่น เช่น Guavaหรือ Apache Commons Langเพื่อเพิ่มฟังก์ชันการทำงานได้ แพ็คเกจทั้งสองนี้มีเมธอด filter() (ใน คลาส Collections2จาก Guava และ CollectionUtilsจาก Apache) filter() วิธีการจะส่งคืนองค์ประกอบที่ตรงกับภาคแสดงที่กำหนด ใน JDK ทุกอย่างซับซ้อนมากขึ้น ข่าวดีก็คือ เพรดิเคตจะถูกเพิ่มใน Java 8 แต่ตอนนี้คุณต้องใช้ Iterator เพื่อวนซ้ำทั้งคอลเลกชัน แน่นอน คุณสามารถเลียนแบบเส้นทางที่ตามด้วย Guava และ Apache ได้โดยทำความคุ้นเคยกับอินเทอร์เฟซเพรดิเคตใหม่ ตอนนี้เราสามารถใช้โค้ดต่อไปนี้เพื่อกรองคอลเลกชัน: 6. วิธีแปลงรายการเป็นชุดอย่างง่ายดายได้อย่างไร? มีสองวิธีในการทำเช่นนี้ ขึ้นอยู่กับว่าคุณต้องการนิยามความเท่าเทียมกันอย่างไร โค้ดชิ้นแรกจะใส่รายการลงใน HashSet การซ้ำกันในกรณีนี้ถูกกำหนดโดย hashCode() เป็นหลัก โดยปกติแล้วจะได้ผล แต่ถ้าคุณต้องการคำนึงถึงเส้นทางการเปรียบเทียบ จะดีกว่าถ้าใช้ส่วนที่สองของโค้ด ซึ่งคุณสามารถกำหนดตัวเปรียบเทียบของคุณเองได้ 7. ฉันจะลบองค์ประกอบที่ซ้ำกันออกจาก ArrayList ได้อย่างไร คำถามนี้ค่อนข้างเกี่ยวข้องกับคำถามข้างต้น หากลำดับขององค์ประกอบใน ArrayList ไม่สำคัญสำหรับคุณ การดำเนินการอย่างชาญฉลาดคือการวางแผ่นงานไว้ในชุดเพื่อลบรายการที่ซ้ำกัน จากนั้นจึงคืนกลับไปยังรายการ ด้านล่างนี้เป็นตัวอย่าง หากลำดับขององค์ประกอบมีความสำคัญต่อคุณ คุณสามารถรับประกันลำดับได้โดยการวางรายการใน LinkedHashSetซึ่งอยู่ใน JDK มาตรฐาน 8. คอลเลกชันที่เรียงลำดับ int[] array = {1,2,3,4,5}; List list = new ArrayList (); for(int i: array) { list.add(i); } Iterator itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } } public interface Predicate { boolean test(T o); } public static void filter(Collection collection, Predicate predicate) { if ((collection != null) && (predicate != null)) { Iterator itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } } filter(list, new Predicate () { public boolean test(Integer i) { return i <= 5; } }); Set set = new HashSet (list); Set set = new TreeSet (aComparator); set.addAll(list); ArrayList** list = ... // initial a list with duplicate elements Set set = new HashSet (list); list.clear(); list.addAll(set); มีหลายวิธีในการสนับสนุนคอลเลกชันที่เรียงลำดับใน Java ทั้งหมดนี้มีการรวบรวมตามลำดับปกติหรือโดยตัวเปรียบเทียบที่ระบุ ในกรณีของลำดับตามธรรมชาติ คุณจะต้องใช้ อินเทอร์เฟซ ที่เปรียบเทียบได้กับองค์ประกอบ ด้วย
  1. Collections.sort()สามารถจัดเรียงรายการได้ ตามที่ระบุไว้ในเอกสารประกอบ Java การเรียงลำดับนี้มีความเสถียรและรับประกันประสิทธิภาพ n log(n)
  2. PriorityQueueจัดให้มีคิวที่เป็นระเบียบ ความแตกต่างระหว่าง PriorityQueue และ Collections.sort() ก็คือ PriorityQueue จะรักษาลำดับของคิวไว้ตลอดเวลา แต่คุณสามารถรับได้เฉพาะองค์ประกอบแรกของคิวเท่านั้น คุณไม่สามารถสุ่มเข้าถึงองค์ประกอบเช่น PriorityQueue.get(4)
  3. หากไม่มีรายการที่ซ้ำกันในคอลเลกชัน คุณสามารถเลือกTreeSet เช่นเดียวกับ PriorityQueue ตรงที่ TreeSet จะรักษาชุดที่เรียงลำดับไว้ตลอดเวลา คุณสามารถรับองค์ประกอบที่เล็กที่สุดหรือใหญ่ที่สุดได้จาก TreeSet แต่คุณยังคงไม่สามารถเข้าถึงองค์ประกอบแบบสุ่มได้
พูดง่ายๆ ก็คือ Collections.sort() จัดเตรียมรายการที่เรียงลำดับเพียงครั้งเดียว PriorityQueue และ TreeSet จะรักษาคอลเลกชันที่เรียงลำดับไว้ตลอดเวลา โดยสูญเสียการเข้าถึงองค์ประกอบที่จัดทำดัชนีไว้ 9. Collections.emptyList() หรืออินสแตนซ์ใหม่ คำถามเดียวกันนี้ใช้กับ EmptyMap() และ EmptySet() ทั้งสองวิธีส่งคืนรายการว่าง แต่ Collections.emptyList() เป็นรายการที่ไม่เปลี่ยนรูป ซึ่งหมายความว่าคุณ ไม่สามารถเพิ่มองค์ประกอบใหม่ลงในรายการ "ว่าง" ได้ ในเบื้องหลัง การเรียกเมธอด Collections.emptyList() แต่ละครั้งไม่ได้สร้างอินสแตนซ์ใหม่ของรายการว่าง แต่จะนำอินสแตนซ์ว่างที่มีอยู่แล้วกลับมาใช้ใหม่แทน หากคุณคุ้นเคยกับ Singleton เป็นรูปแบบ การออกแบบคุณควรเข้าใจว่ามันหมายถึงอะไร สิ่งนี้ควรให้ ประสิทธิภาพ ที่ดีขึ้นหากถูกเรียกบ่อยครั้ง 10 การคัดลอกคอลเลกชัน Collections.copy() มีสองวิธีในการคัดลอกรายการแหล่งที่มาไปยังรายการปลายทาง วิธีหนึ่งคือการใช้ตัวสร้าง ArrayList อีกวิธีหนึ่ง คือ การใช้ เมธอด Collections.copy() ประกาศในบรรทัดแรก: เรากำลังจัดสรรรายการที่มีความยาวอย่างน้อยเท่ากับความยาวของรายการดั้งเดิม เนื่องจากเอกสาร Java เกี่ยวกับคอลเลกชันระบุว่า: ArrayList dstList = new ArrayList (srcList);
รายการปลายทางต้องมีอย่างน้อยเท่ากับรายการต้นทาง
ซึ่งหมายความว่ารายการสุดท้ายจะต้องไม่สั้นไปจากรายการเดิม ทั้งสองวิธีเป็นการคัดลอกแบบตื้น แล้วสองวิธีนี้แตกต่างกันอย่างไร? ขั้นแรก Collections.copy() จะไม่จัดสรรความจุของคอลเลกชัน dstList ใหม่ แม้ว่า dstList จะมีพื้นที่ไม่เพียงพอที่จะเก็บองค์ประกอบทั้งหมดจาก srcList ก็ตาม แต่จะโยน IndexOutOfBoundsExceptionแทน อาจมีคนถามว่ามีประโยชน์อะไรบ้างในเรื่องนี้ เหตุผลก็คือเพื่อให้แน่ใจว่าวิธีการทำงานเป็นเส้นตรงตรงเวลา นอกจากนี้ยังเหมาะสมเมื่อคุณต้องการใช้อาร์เรย์ซ้ำแทนที่จะจัดสรรหน่วยความจำในตัวสร้าง ArrayList ใหม่ แทนที่จะสรุป ว่าหลังจากอ่านบทความแล้วคุณยังมีคำถามอยู่โปรดถามพวกเขาในความคิดเห็น นอกจากนี้ หากคุณพบความไม่ถูกต้องในการแปลหรือข้อผิดพลาดอื่นใด โปรดเขียนถึง PM เราจะแก้ไขให้ถูกต้อง และคุณจะได้รับการขอบคุณ ต้นฉบับ. ArrayList dstList = new ArrayList (srcList.size()); Collections.copy(dstList, srcList);
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION