เมื่อเร็ว ๆ นี้ ฉันเห็นคำอธิบายที่ดีของกรอบงานคอลเลกชันในหนังสือ J2EE ฉันกรองและโพสต์ไว้เพื่อแบ่งปันกับทุกคน กรอบงานคอลเลกชันจัดเตรียมอินเทอร์เฟซและคลาสสำหรับการจัดการคอลเลกชันวัตถุ ต่อไปนี้เป็น Description ของแต่ละองค์ประกอบ
อินเตอร์เฟซการรวบรวม
คอลเลกชันเป็นอินเทอร์เฟซคอลเลกชันพื้นฐานที่สุด คอลเลกชันแสดงถึงชุดของออบเจ็กต์ นั่นคือ องค์ประกอบของคอลเลกชัน คอลเลกชันบางรายการอนุญาตให้มีองค์ประกอบที่เหมือนกันและบางคอลเลกชันไม่อนุญาต บางประเภทและบางประเภทไม่ทำ Java SDK ไม่ได้จัดเตรียมคลาสที่สืบทอดมาจากคอลเลคชันโดยตรง คลาสที่ Java SDK มอบให้นั้นเป็น "อินเทอร์เฟซย่อย" ทั้งหมดที่สืบทอดมาจากคอลเลคชัน เช่น รายการและเซ็ต
คลาสทั้งหมดที่ใช้อินเทอร์เฟซคอลเลกชันจะต้องมีตัวสร้างมาตรฐานสองตัว: ตัวสร้างแบบไม่มีพารามิเตอร์สำหรับการสร้างคอลเลกชันที่ว่างเปล่า และตัวสร้างพารามิเตอร์ของคอลเลกชันสำหรับการสร้างคอลเลกชันใหม่ คอลเลกชันอินพุตมีองค์ประกอบเดียวกัน ตัวสร้างหลังอนุญาตให้ผู้ใช้คัดลอกคอลเลกชัน
จะวนซ้ำแต่ละองค์ประกอบในคอลเลกชันได้อย่างไร ไม่ว่าคอลเลกชันที่แท้จริงจะเป็นประเภทใด แต่ก็รองรับเมธอด iterator() ซึ่งส่งคืนตัววนซ้ำที่สามารถใช้เพื่อเข้าถึงแต่ละองค์ประกอบในคอลเลกชันทีละรายการ การใช้งานทั่วไปมีดังนี้:
คัดลอกรหัสรหัส ดังต่อไปนี้:
Iterator it = collection.iterator(); // รับตัววนซ้ำ
ในขณะที่(it.hasNext()) {
Object obj = it.next(); // รับองค์ประกอบถัดไป
-
อินเทอร์เฟซทั้งสองที่ได้รับจากอินเทอร์เฟซคอลเลกชันคือรายการและตั้งค่า
อินเตอร์เฟซรายการ รายการเป็นคอลเลกชันที่เรียงลำดับ เมื่อใช้อินเทอร์เฟซนี้ คุณจะสามารถควบคุมตำแหน่งการแทรกของแต่ละองค์ประกอบได้อย่างแม่นยำ ผู้ใช้สามารถเข้าถึงองค์ประกอบในรายการโดยใช้ดัชนี (ตำแหน่งขององค์ประกอบในรายการ คล้ายกับตัวห้อยอาร์เรย์) ซึ่งคล้ายกับอาร์เรย์ Java
ไม่เหมือนกับชุดที่กล่าวถึงด้านล่าง List อนุญาตให้ใช้องค์ประกอบเดียวกัน
นอกเหนือจากเมธอด iterator() ที่จำเป็นสำหรับอินเทอร์เฟซ Collection แล้ว List ยังมีเมธอด listIterator() ซึ่งส่งคืนอินเทอร์เฟซ ListIterator เมื่อเปรียบเทียบกับอินเทอร์เฟซ Iterator มาตรฐาน ListIterator ยังมีวิธี add() เพิ่มเติมและวิธีอื่น ๆ ที่อนุญาตให้เพิ่ม ลบ ตั้งค่าองค์ประกอบ และเคลื่อนที่ไปข้างหน้าหรือข้างหลัง
คลาสทั่วไปที่ใช้อินเทอร์เฟซรายการคือ LinkedList, ArrayList, Vector และ Stack
คลาส LinkedList LinkedList ใช้อินเทอร์เฟซรายการและอนุญาตให้มีองค์ประกอบที่เป็นโมฆะ นอกจากนี้ LinkedList ยังมีวิธีการรับ ลบ และแทรกเพิ่มเติมที่ส่วนหัวหรือส่วนท้ายของ LinkedList การดำเนินการเหล่านี้อนุญาตให้ใช้ LinkedList เป็นสแต็ก คิว หรือ deque
โปรดทราบว่า LinkedList ไม่มีวิธีการซิงโครไนซ์ หากหลายเธรดเข้าถึงรายการพร้อมกัน พวกเขาจะต้องดำเนินการซิงโครไนซ์การเข้าถึงด้วยตนเอง วิธีแก้ปัญหาหนึ่งคือการสร้างรายการที่ซิงโครไนซ์เมื่อสร้างรายการ:
รายการรายการ = Collections.synchronizedList(new LinkedList(...));
คลาส ArrayList ArrayList ใช้อาร์เรย์ขนาดตัวแปร อนุญาตให้ใช้องค์ประกอบทั้งหมด รวมถึงค่าว่างด้วย ArrayList ไม่ได้รับการซิงโครไนซ์
เวลาทำงานของขนาด isEmpty, get และ set วิธีการจะคงที่ อย่างไรก็ตาม ต้นทุนของวิธีเพิ่มเป็นค่าคงที่ที่ตัดจำหน่าย และการเพิ่มองค์ประกอบ n รายการต้องใช้เวลา O(n) วิธีอื่นมีเวลาในการทำงานเป็นเส้นตรง
แต่ละอินสแตนซ์ ArrayList มีความจุ (Capacity) ซึ่งเป็นขนาดของอาร์เรย์ที่ใช้จัดเก็บองค์ประกอบ ความจุนี้จะเพิ่มขึ้นโดยอัตโนมัติเมื่อมีการเพิ่มองค์ประกอบใหม่ แต่ไม่ได้กำหนดอัลกอริทึมการเติบโต เมื่อจำเป็นต้องแทรกองค์ประกอบจำนวนมาก สามารถเรียกใช้เมธอด SureCapacity เพื่อเพิ่มความจุของ ArrayList ก่อนที่จะแทรกเพื่อปรับปรุงประสิทธิภาพการแทรก
เช่นเดียวกับ LinkedList ArrayList ก็ไม่ได้รับการซิงโครไนซ์เช่นกัน
คลาสเวกเตอร์ Vector คล้ายกับ ArrayList มาก แต่ Vector ได้รับการซิงโครไนซ์แล้ว แม้ว่า Iterator ที่สร้างโดย Vector จะมีอินเทอร์เฟซเหมือนกับ Iterator ที่สร้างโดย ArrayList เนื่องจาก Vector ได้รับการซิงโครไนซ์ เมื่อมีการสร้าง Iterator และกำลังใช้งาน เธรดอื่นจะเปลี่ยนสถานะของ Vector (เช่น การเพิ่มหรือลบองค์ประกอบบางส่วน) , ConcurrentModificationException จะถูกส่งออกมาเมื่อเรียกใช้เมธอด Iterator ดังนั้นจึงต้องตรวจพบข้อยกเว้น
คลาสสแต็ค สแต็กสืบทอดมาจาก Vector และใช้สแต็กเข้าก่อนออกก่อน Stack มี 5 วิธีเพิ่มเติมที่อนุญาตให้ Vector สามารถใช้เป็นสแต็กได้ วิธีการพุชและป๊อปพื้นฐาน เช่นเดียวกับวิธีดู จะรับองค์ประกอบที่ด้านบนของสแต็ก วิธีว่างเปล่าจะทดสอบว่าสแต็กว่างเปล่าหรือไม่ และวิธีการค้นหาจะตรวจจับตำแหน่งขององค์ประกอบในสแต็ก Stack คือสแต็กว่างหลังจากที่ถูกสร้างขึ้น
ตั้งค่าอินเทอร์เฟซ Set คือคอลเลกชันที่ไม่มีองค์ประกอบที่ซ้ำกัน กล่าวคือ สององค์ประกอบใดๆ e1 และ e2 มี e1.equals(e2)=false และ Set มีองค์ประกอบ null มากสุดหนึ่งองค์ประกอบ
แน่นอนว่า Set Constructor มีข้อจำกัดว่าพารามิเตอร์ Collection ที่ส่งผ่านเข้ามาต้องไม่มีองค์ประกอบที่ซ้ำกัน
โปรดทราบ: วัตถุที่ไม่แน่นอนจะต้องได้รับการจัดการด้วยความระมัดระวัง หากองค์ประกอบที่ไม่แน่นอนในชุดเปลี่ยนสถานะทำให้เกิด Object.equals(Object)=true ก็จะทำให้เกิดปัญหาบางอย่าง
อินเทอร์เฟซแผนที่ <BR>โปรดทราบว่าแผนที่ไม่สืบทอดอินเทอร์เฟซคอลเลกชัน แผนที่ให้คีย์ในการแมปค่า แผนที่ไม่สามารถมีคีย์เดียวกันได้ และแต่ละคีย์สามารถแมปได้เพียงค่าเดียวเท่านั้น อินเทอร์เฟซแผนที่มีมุมมองการตั้งค่าสามประเภท เนื้อหาของแผนที่ถือได้ว่าเป็นชุดของชุดคีย์ ชุดของชุดค่า หรือชุดของการแมปคีย์-ค่า
คลาสแฮชเทเบิล Hashtable สืบทอดอินเทอร์เฟซ Map และใช้ตารางแฮชของการแมปคีย์-ค่า วัตถุที่ไม่ใช่ null ใดๆ สามารถใช้เป็นคีย์หรือค่าได้
หากต้องการเพิ่มข้อมูล ให้ใช้ put(key, value) และหากต้องการลบข้อมูล ให้ใช้ get(key) ต้นทุนเวลาของการดำเนินการพื้นฐานทั้งสองนี้จะคงที่
Hashtable ปรับประสิทธิภาพผ่านพารามิเตอร์สองตัว: ความจุเริ่มต้นและปัจจัยโหลด โดยปกติแล้ว ค่าโหลดแฟกเตอร์เริ่มต้น 0.75 จะทำให้มีความสมดุลระหว่างเวลาและพื้นที่ดีขึ้น การเพิ่มตัวประกอบการโหลดสามารถประหยัดพื้นที่ได้ แต่เวลาในการค้นหาที่สอดคล้องกันจะเพิ่มขึ้น ซึ่งจะส่งผลต่อการดำเนินการ เช่น การรับและการวาง
ตัวอย่างง่ายๆ ของการใช้ Hashtable มีดังนี้ ใส่ 1, 2 และ 3 ลงใน Hashtable และคีย์คือ "หนึ่ง", "สอง" และ "สาม" ตามลำดับ:
คัดลอกรหัสรหัส ดังต่อไปนี้:
หมายเลข Hashtable = ใหม่ Hashtable();
Numbers.put("หนึ่ง", จำนวนเต็มใหม่ (1));
Numbers.put("สอง", จำนวนเต็มใหม่ (2));
Numbers.put("สาม", จำนวนเต็มใหม่ (3));
หากต้องการดึงข้อมูลตัวเลข เช่น 2 ให้ใช้คีย์ที่เกี่ยวข้อง:
จำนวนเต็ม n = (จำนวนเต็ม)numbers.get("สอง");
System.out.println("สอง = " + n);
เนื่องจากวัตถุที่ใช้เป็นคีย์จะกำหนดตำแหน่งของค่าที่สอดคล้องกันโดยการคำนวณฟังก์ชันแฮชของมัน วัตถุใดๆ ที่ใช้เป็นคีย์จะต้องใช้วิธี hashCode และเท่ากับ hashCode และเมธอดเท่ากับสืบทอดมาจากคลาสรูท Object หากคุณใช้คลาสแบบกำหนดเองเป็นคีย์ ให้ระวังให้มาก ตามคำจำกัดความของฟังก์ชันแฮช หากทั้งสองอ็อบเจ็กต์เหมือนกัน นั่นคือ obj1.equals( obj2)=true ดังนั้น hashCode ของพวกเขาจะต้องเหมือนกัน แต่ถ้าทั้งสองวัตถุแตกต่างกัน hashCode ของพวกเขาก็ไม่จำเป็นต้องแตกต่างกัน หาก hashCode ของสองวัตถุที่แตกต่างกันจะเหมือนกัน ปรากฏการณ์นี้เรียกว่าความขัดแย้ง ค่าใช้จ่ายในการดำเนินการตารางแฮชจะเพิ่มขึ้น ดังนั้นให้ลองกำหนดวิธีการ hashCode() ที่กำหนดไว้อย่างดีเพื่อเพิ่มความเร็วในการดำเนินการตารางแฮช
หากออบเจ็กต์เดียวกันมี hashCode ที่แตกต่างกัน การทำงานของตารางแฮชจะมีผลลัพธ์ที่ไม่คาดคิด (เมธอด get ที่คาดหวังจะส่งกลับค่า null) เพื่อหลีกเลี่ยงปัญหานี้ คุณจะต้องจำสิ่งเดียวเท่านั้น: แทนที่เมธอดเท่ากับและเมธอด hashCode พร้อมกัน เวลา อย่าเขียนเพียงหนึ่งในนั้น
Hashtable เป็นแบบซิงโครนัส
คลาส HashMap HashMap คล้ายกับ Hashtable ยกเว้นว่า HashMap เป็นแบบอะซิงโครนัสและอนุญาตให้มีค่า null นั่นคือค่า null และคีย์ null แต่เมื่อปฏิบัติต่อ HashMap เสมือนเป็นคอลเลกชั่น (เมธอด Values() สามารถส่งคืนคอลเลกชั่นได้) ค่าใช้จ่ายด้านเวลาของการดำเนินการย่อยของการวนซ้ำจะเป็นสัดส่วนกับความจุของ HashMap ดังนั้น หากประสิทธิภาพของการดำเนินการวนซ้ำมีความสำคัญมาก อย่าตั้งค่าความจุเริ่มต้นของ HashMap สูงเกินไปหรือปัจจัยโหลดต่ำเกินไป
คลาส WeakHashMap WeakHashMap เป็น HashMap ที่ได้รับการปรับปรุงซึ่งใช้ "การอ้างอิงที่ไม่รัดกุม" กับคีย์ หากไม่มีการอ้างอิงคีย์จากภายนอกอีกต่อไป GC สามารถรีไซเคิลคีย์ได้
สรุป <BR>หากเกี่ยวข้องกับการดำเนินการ เช่น สแต็กและคิว คุณควรพิจารณาใช้รายการ หากคุณต้องการแทรกและลบองค์ประกอบอย่างรวดเร็ว คุณควรใช้ LinkedList หากคุณต้องการเข้าถึงองค์ประกอบแบบสุ่มอย่างรวดเร็ว คุณควรใช้ ArrayList
หากโปรแกรมอยู่ในสภาพแวดล้อมแบบเธรดเดียว หรือเข้าถึงได้ดำเนินการในเธรดเดียวเท่านั้น ให้พิจารณาคลาสแบบอะซิงโครนัสซึ่งมีประสิทธิภาพมากกว่า หากหลายเธรดอาจดำเนินการคลาสเดียวในเวลาเดียวกัน ควรใช้คลาสที่ซิงโครไนซ์
ให้ความสนใจเป็นพิเศษกับการทำงานของตารางแฮช วัตถุที่ใช้เป็นคีย์จะต้องแทนที่เมธอดเท่ากับและ hashCode อย่างถูกต้อง
พยายามส่งคืนอินเทอร์เฟซแทนที่จะเป็นประเภทจริง เช่น การส่งคืนรายการแทน ArrayList เพื่อที่ว่าหากคุณต้องการแทนที่ ArrayList ด้วย LinkedList ในอนาคต รหัสไคลเอ็นต์ก็ไม่จำเป็นต้องเปลี่ยนแปลง นี่คือการเขียนโปรแกรมสำหรับนามธรรม