HashMap และ Hashset เป็นสมาชิกสำคัญสองคนของกรอบคอลเลกชัน Java HASHMAP เป็นคลาสการใช้งานทั่วไปสำหรับอินเตอร์เฟส MAP และ HashSet เป็นคลาสการใช้งานทั่วไปสำหรับอินเทอร์เฟซที่ตั้งไว้ แม้ว่าข้อกำหนดของอินเทอร์เฟซที่ใช้โดย HashMap และ HashSet นั้นแตกต่างกัน แต่กลไกการจัดเก็บแฮชพื้นฐานของพวกเขานั้นเหมือนกันทุกประการ
ในความเป็นจริงมีความคล้ายคลึงกันมากมายระหว่าง Hashset และ HashMap สำหรับ HashSet ระบบใช้อัลกอริทึมแฮชเพื่อกำหนดตำแหน่งการจัดเก็บขององค์ประกอบการรวบรวมเพื่อให้แน่ใจว่าองค์ประกอบการรวบรวมสามารถจัดเก็บและเรียกคืนได้อย่างรวดเร็ว สำหรับ HASHMAP ค่าคีย์ของระบบจะถูกประมวลผลโดยรวมและระบบจะคำนวณตำแหน่งการจัดเก็บของค่าคีย์ตามอัลกอริทึมแฮชเสมอเพื่อให้คู่คีย์-ค่าของแผนที่สามารถเก็บและดึงข้อมูลได้อย่างรวดเร็ว
ก่อนที่จะแนะนำที่เก็บข้อมูลคอลเลกชันควรชี้ให้เห็นว่าแม้ว่าคอลเลกชันอ้างว่าเก็บวัตถุ Java แต่ก็ไม่ได้ใส่วัตถุ Java ลงในคอลเลกชันที่ตั้งไว้ แต่จะยังคงอ้างอิงถึงวัตถุเหล่านี้ในชุดสะสม กล่าวคือคอลเลกชัน Java เป็นชุดของตัวแปรอ้างอิงหลายตัวที่ชี้ไปที่วัตถุ Java จริง
1. คุณสมบัติพื้นฐานของ hashmap
หลังจากอ่านความคิดเห็นในซอร์สโค้ด jdk hashmap.class คุณสามารถสรุปคุณสมบัติ HashMap ได้มากมาย
HashMap อนุญาตให้ทั้งคีย์และค่าเป็นโมฆะในขณะที่ Hashtable ไม่ได้
hashmap เป็นเธรด-ไม่ปลอดภัยในขณะที่แฮชแต้มเป็นเธรดที่ปลอดภัย
ลำดับขององค์ประกอบใน HASHMAP ไม่เหมือนกันเสมอไปและตำแหน่งขององค์ประกอบเดียวกันอาจเปลี่ยนแปลงได้ตลอดเวลา (กรณีของการปรับขนาด)
ความซับซ้อนของเวลาในการสำรวจ HASHMAP นั้นเป็นสัดส่วนกับความสามารถและจำนวนองค์ประกอบที่มีอยู่ หากคุณต้องการให้แน่ใจว่าประสิทธิภาพของการเดินทางผ่านความจุเริ่มต้นไม่สามารถตั้งค่าได้สูงเกินไปหรือไม่สามารถตั้งค่าปัจจัยสมดุลต่ำเกินไปได้
เช่นเดียวกับรายการที่เกี่ยวข้องก่อนหน้านี้เนื่องจาก HashMap เป็นเธรด-ความปลอดภัยการล้มเหลวจะถูกสร้างขึ้นเมื่อตัววนซ้ำพยายามเปลี่ยนโครงสร้างคอนเทนเนอร์ในระหว่างกระบวนการวนซ้ำ สามารถรับ hashmap ที่ซิงโครไนซ์ผ่านคอลเลกชัน SynchronizedMap (HashMap)
2. การวิเคราะห์โครงสร้างข้อมูลตารางแฮช
ตารางแฮช (ตารางแฮช, แฮชตาราง) เป็นโครงสร้างข้อมูลที่เข้าถึงตำแหน่งที่เก็บข้อมูลหน่วยความจำโดยตรงตามคำหลัก กล่าวคือตารางแฮชสร้างการแมปโดยตรงระหว่างคำหลักและที่อยู่จัดเก็บข้อมูล
ดังที่แสดงในรูปด้านล่างคีย์ได้รับตำแหน่งดัชนีของถังผ่านฟังก์ชันแฮช
การได้รับดัชนีผ่านฟังก์ชั่นแฮชจะเกิดขึ้นในสถานการณ์เดียวกันอย่างหลีกเลี่ยงไม่ได้นั่นคือความขัดแย้ง นี่คือวิธีการแก้ไขข้อขัดแย้ง:
การเปิดที่อยู่: แนวคิดพื้นฐานของวิธีนี้คือการสแกนตำแหน่งภายใต้ตารางตามลำดับเมื่อพบความขัดแย้งและกรอกหากมีฟรี อัลกอริทึมเฉพาะจะไม่ถูกอธิบายอีกต่อไปต่อไปนี้เป็นแผนภาพแผนผัง:
แยกการผูกมัด: แนวคิดพื้นฐานของวิธีนี้คือการรวมรายการเข้าด้วยกันด้วยค่าดัชนีเดียวกันในรายการที่เชื่อมโยงเมื่อพบความขัดแย้ง อัลกอริทึมเฉพาะจะไม่ถูกอธิบายอีกต่อไปต่อไปนี้เป็นแผนภาพแผนผัง:
วิธีการแก้ไขความขัดแย้งใน HashMap ใน JDK คือการใช้วิธีการผูกมัดแยกต่างหาก
3. การวิเคราะห์ซอร์สโค้ด HashMap (JDK1.7)
1. องค์ประกอบการอ่านและเขียน HashMap
รายการ
องค์ประกอบที่เก็บไว้ใน HashMap เป็นประเภทรายการ ซอร์สโค้ดของรายการในซอร์สโค้ดได้รับด้านล่าง:
รายการคลาสคงที่ <k, v> ใช้ map.entry <k, v> {คีย์สุดท้าย k; ค่า V; รายการ <k, v> ถัดไป; แฮช int; รายการ (int h, k k, v v, รายการ <k, v> n) {value = v; ถัดไป = n; key = k; แฮช = h; } // วิธีการรับและชุดของคีย์ค่าจะถูกละเว้นการดำเนินการรับและการตั้งค่าจะถูกใช้ในตัววนซ้ำที่ตามมา ... บูลีนสุดท้ายของสาธารณะเท่ากับ (วัตถุ o) {ถ้า (! map.entry e = (map.entry) o; วัตถุ K1 = getKey (); Object K2 = E.getKey (); if (k1 == k2 || (k1! = null && k1.equals (k2))) {object v1 = getValue (); Object V2 = E.getValue (); if (v1 == v2 || (v1! = null && v1.equals (v2))) ส่งคืนจริง; } return false; } // ที่นี่ทำหรือใช้งาน hashcode ของคีย์และ hashcode ของค่าเพื่อรับ hashcode ของรายการสาธารณะสุดท้าย int hashcode () {return objects.hashcode (getkey ()) ^ objects.hashcode (getValue ()); } public สตริงสุดท้าย toString () {return getKey () + "=" + getValue (); } /** * วิธีการนี้จะถูกเรียกใช้เมื่อใดก็ตามที่ค่าในรายการนั้นถูกเขียนทับโดยการเรียกใช้ (k, v) สำหรับคีย์ k ที่มีอยู่แล้ว * ใน hashmap * / Void RecordAccess (HashMap <K, V> M) {} / ** * วิธีนี้จะถูกเรียกใช้เมื่อใดก็ตามที่รายการถูกลบออกจากตาราง */ void recordremoval (hashmap <k, v> m) {}}รายการรวมถึงคีย์, ค่า, แฮชและการอ้างอิงไปยังรายการถัดไป เห็นได้ชัดว่านี่เป็นรายการที่เชื่อมโยงเดียวซึ่งใช้อินเตอร์เฟส map.entry
RecordAcess (HashMap <K, V> และ RecordRemoval (HashMap <K, V>) ไม่ได้ถูกนำมาใช้ใน HASHMAP อย่างไรก็ตามทั้งสองวิธีของ LinkedHashMap ใช้เพื่อใช้อัลกอริทึม LRU
รับ: อ่านองค์ประกอบเพื่อรับรายการที่เกี่ยวข้องจาก HashMap ต่อไปนี้เป็นซอร์สโค้ดของ GET:
สาธารณะ v รับ (คีย์วัตถุ) {// สถานการณ์ที่คีย์เป็นโมฆะถ้า (key == null) return getForNullKey (); // ค้นหารายการตามรายการคีย์ <k, v> entry = getEntry (คีย์); ส่งคืน null == รายการ? null: entry.getValue (); -ซอร์สโค้ด getfornullkey
ส่วนตัว v getForNullKey () {ถ้า (ขนาด == 0) {return null; } // transtraighten ห่วงโซ่ความขัดแย้งสำหรับ (รายการ <k, v> e = ตาราง [0]; e! = null; e = e.next) {ถ้า (e.key == null) ส่งคืน e.value; } return null; -รายการที่มีคีย์ null ถูกเก็บไว้ในตาราง [0] แต่ห่วงโซ่ความขัดแย้งในตาราง [0] ไม่จำเป็นต้องมีคีย์เป็นโมฆะดังนั้นจึงจำเป็นต้องถูกสำรวจ
รับรายการตามคีย์:
รายการสุดท้าย <k, v> getentry (คีย์วัตถุ) {ถ้า (ขนาด == 0) {return null; } int hash = (key == null)? 0: แฮช (กุญแจ); // รับตำแหน่งดัชนีในตารางผ่านแฮชจากนั้นสำรวจรายการที่เชื่อมโยงที่ขัดแย้งกันเพื่อค้นหาคีย์สำหรับ (รายการ <k, v> e = ตาราง [indexfor (แฮช, table.length)]; e! = null; e = e.next) {object k; if (e.hash == hash && ((k = e.key) == key || (key! = null && key.equals (k)))) กลับ e; } return null; -ข้างต้นเป็นกระบวนการของ HashMap ที่อ่านรายการและซอร์สโค้ด ความซับซ้อนของเวลา o (1)
ใส่: องค์ประกอบการเขียน
การดำเนินการใส่ใน HASHMAP ค่อนข้างซับซ้อนเนื่องจากจะมีการดำเนินการขยาย HASHMAP ในระหว่างการดำเนินการ
เมื่อมีการเขียนองค์ประกอบใหม่หากมีคีย์ในการเขียนองค์ประกอบใน HASHMAP การดำเนินการของการแทนที่ค่าจะดำเนินการซึ่งเทียบเท่ากับการอัปเดต นี่คือซอร์สโค้ดที่ใส่:
Public V Put (k key, v value) {// ถ้าตารางว่างเปล่าให้เติมถ้า (ตาราง == emport_table) ตามเกณฑ์ของขนาด {พองได้ (เกณฑ์); } // เติมรายการด้วยคีย์เป็น null ถ้า (key == null) return putfornullkey (value); // สร้างแฮชเพื่อรับการแมปของดัชนีดัชนี int hash = แฮช (คีย์); int i = indexfor (แฮช, table.length); // transultion ห่วงโซ่ความขัดแย้งของดัชนีปัจจุบันเพื่อค้นหาว่ามีคีย์ที่สอดคล้องกันสำหรับ (รายการ <k, v> e = ตาราง [i]; e! = null; e = e.next) {วัตถุ K; // ถ้าคีย์ที่เกี่ยวข้องมีอยู่ให้แทนที่ oldValue และส่งคืน oldValue ถ้า (e.hash == hash && ((k = e.key) == key || key.equals (k))) {v oldValue = e.value; e.value = ค่า; E.RecordAccess (นี่); กลับ OldValue; }} // คีย์ของรายการที่เขียนใหม่ไม่มีอยู่ในห่วงโซ่ความขัดแย้ง ModCount ++; // แทรกรายการใหม่ของรายการ (แฮช, คีย์, ค่า, i); คืนค่า null; -Addentry และ CreateEntry Source:
โมฆะ addentry (int hash, k key, v ค่า V, int bucketindex) {// ก่อนที่จะแทรกรายการใหม่ให้ตัดสินขนาดของแฮชเมปปัจจุบันและขนาดเกณฑ์ของมันและเลือกว่าจะขยายถ้า (ขนาด> = เกณฑ์) && (null! = ตาราง [bucketindex]) hash = (null! = key)? แฮช (กุญแจ): 0; BucketIndex = indexfor (แฮช, table.length); } createentry (แฮชคีย์ค่า bucketindex); } เป็นโมฆะ createentry (int hash, k key, v ค่า V, int bucketindex) {entry <k, v> e = ตาราง [bucketindex]; // วิธีการแทรกหัวรายการที่เขียนใหม่จะถูกแทรกเข้าไปในด้านหน้าของรายการแรกในห่วงโซ่ความขัดแย้งที่ตำแหน่งดัชนีปัจจุบัน ตาราง [BucketIndex] = รายการใหม่ <> (แฮช, คีย์, ค่า, e); ขนาด ++; -ข้างต้นเป็นกระบวนการเขียนรายการลงใน HASHMAP และซอร์สโค้ด ความซับซ้อนของเวลา o (1)
ลบลบองค์ประกอบ:
รายการสุดท้าย <k, v> removeentryforkey (คีย์วัตถุ) {ถ้า (ขนาด == 0) {return null; } // คำนวณค่าแฮชตามคีย์และรับดัชนี int hash = (key == null)? 0: แฮช (กุญแจ); int i = indexfor (แฮช, table.length); // ลบรายการที่เชื่อมโยงกำหนดสองพอยน์เตอร์ pre แสดงรายการก่อนหน้า <k, v> prev = ตาราง [i]; รายการ <k, v> e = prev; // transtraight ห่วงโซ่ความขัดแย้งและลบคีย์ทั้งหมดในขณะที่ (e! = null) {entry <k, v> next = e.next; วัตถุ K; // ถ้าพบ (e.hash == hash && ((k = ekey) == key || (key! = null && key.equals (k)))) {modcount ++; ขนาด--; // การค้นหาโหนดแรกคือโหนดที่จะถูกลบหาก (prev == e) ตาราง [i] = ถัดไป; else prev.next = ถัดไป; E.recordremoval (นี่); กลับ E; } prev = e; e = ถัดไป; } return e; -ข้างต้นเป็นกระบวนการของการลบ hashmap การลบรายการและซอร์สโค้ด ความซับซ้อนของเวลา o (1)
2. หลักการแฮชแฮช (ฟังก์ชั่นแฮช)
การใช้งานฟังก์ชั่นแฮชใน HashMap นั้นทำผ่านแฮช (วัตถุ K) และดัชนีสำหรับ (int h, ความยาว int) มาดูซอร์สโค้ดด้านล่าง:
hash int สุดท้าย (วัตถุ K) {int h = hashseed; if (0! = h && k อินสแตนซ์ของสตริง) {return sun.misc.hashing.stringhash32 ((สตริง) k); } h ^= k.hashCode (); // ฟังก์ชั่นนี้ทำให้มั่นใจได้ว่า hashcodes ที่แตกต่างกันโดย // ทวีคูณคงที่ในแต่ละตำแหน่งบิตมีขอบเขต // จำนวนการชน (ประมาณ 8 ที่ปัจจัยโหลดเริ่มต้น) // เพื่อลดโอกาสของความขัดแย้ง h ^ = (h >>> 20) ^ (h >>> 12); กลับ h ^ (h >>> 7) ^ (h >>> 4); -รับซอร์สโค้ดดัชนีดัชนี:
intative int indexfor (int h, ความยาว int) {// assert integer.bitCount (ความยาว) == 1: "ความยาวต้องเป็นพลังงานที่ไม่ใช่ศูนย์ของ 2"; กลับ H & (length-1); -แฮชแมปแผนที่ปุ่มไปยังดัชนีภายในช่วงเวลาของ [0, table.length] ผ่านฟังก์ชั่นแฮช โดยทั่วไปมีวิธีการจัดทำดัชนีสองชนิด:
แฮช (คีย์) % ตารางความยาวซึ่งความยาวจะต้องเป็นจำนวนที่สำคัญ Hashtable ใช้การใช้งานนี้ใน JDK
ด้วยเหตุผลเฉพาะสำหรับการใช้ตัวเลขที่สำคัญคุณสามารถค้นหาหลักฐานอัลกอริทึมที่เกี่ยวข้องซึ่งจะไม่ระบุไว้ที่นี่
แฮช (คีย์) & (table.length - 1) โดยที่ความยาวจะต้องเป็นพลังงานแบบเอ็กซ์โปเนนเชียล 2 ตัว HASHMAP ใน JDK ใช้วิธีการใช้งานนี้
เนื่องจากขนาดของความยาวเป็น 2 ครั้งแบบเอ็กซ์โปเนนเชียล, แฮช (คีย์) & (ตารางความยาว - 1) จะอยู่ระหว่าง [0, ความยาว - 1] เสมอ อย่างไรก็ตามหากคุณทำสิ่งนี้เพียงอย่างเดียวจะมีปัญหาใหญ่กับความขัดแย้งเพราะมูลค่าของแฮชโคดในชวาคือ 32 บิต เมื่อความสามารถของ HashMap มีขนาดเล็กเช่นเมื่อ 16 เมื่อทำการดำเนินการ XOR บิตสูงจะถูกยกเลิกเสมอ แต่หลังจากการดำเนินการบิตต่ำความน่าจะเป็นของความขัดแย้งจะเพิ่มขึ้น
ดังนั้นเพื่อลดความน่าจะเป็นของการเกิดความขัดแย้งการดำเนินการบิตจำนวนมากและการดำเนินการพิเศษหรือการดำเนินการจะดำเนินการในรหัส
3. กลยุทธ์การจัดสรรหน่วยความจำ HashMap
ความจุตัวแปรของสมาชิกและตัวโหลด
กำลังการผลิตที่ต้องการคือทวีคูณแบบเอ็กซ์โปเนนเชียล 2 ใน HashMap และความจุเริ่มต้นคือ 1 << 4 = 16 นอกจากนี้ยังมีปัจจัยสมดุล (loadfactor) ใน HASHMAP ปัจจัยที่สูงมากเกินไปจะลดพื้นที่เก็บข้อมูล แต่เวลาในการค้นหา (การค้นหารวมถึงวิธีการใส่และรับใน HashMap) จะเพิ่มขึ้น ค่าเริ่มต้นของ loadfactor คือ 0.75 ซึ่งเป็นค่าที่เหมาะสมที่สุดที่กำหนดโดยการชั่งน้ำหนักความซับซ้อนของเวลาและความซับซ้อนของพื้นที่
int สุดท้าย int default_initial_capacity = 1 << 4; // aka 16 int สุดท้าย int maximum_capacity = 1 << 30; คงที่สุดท้ายลอย default_load_factor = 0.75f;
ตัวสร้าง hashmap
การสร้าง HashMap คือการตั้งค่าความจุและค่าเริ่มต้นของ loadfactor
public hashmap (int initialcapacity, float loadfactor) {ถ้า (initialcapacity <0) โยน unlegalargumentException ใหม่ ("ความจุเริ่มต้นที่ผิดกฎหมาย:" + initialcapacity); if (initialCapacity> maximum_capacity) การเริ่มต้นการเริ่มต้น = maximum_capacity; if (loadfactor <= 0 || float.isnan (loadfactor)) โยน unlegalargumentException ใหม่ ("ปัจจัยโหลดที่ผิดกฎหมาย:" + loadfactor); this.loadfactor = loadfactor; threshold = initialCapacity; init (); - ฉันได้กล่าวไว้ก่อนหน้าความสามารถใน HashMap จะต้องเป็นช่วงเวลาทวีคูณของ 2 และไม่มีการ จำกัด ในตัวสร้าง ดังนั้นวิธีการตรวจสอบให้แน่ใจว่ามูลค่ากำลังการผลิตเป็นเวลาแบบเอ็กซ์โปเนนเชียลที่ 2?
ในระหว่างการดำเนินการซอร์สโค้ดจะพิจารณาว่าตารางแฮชปัจจุบันเป็นตารางที่ว่างเปล่า ถ้าเป็นเช่นนั้นโทรหาฉันได้ (int tosize)
โมฆะส่วนตัวพองได้ (int tosize) {// ค้นหาพลังของ 2> = tosize int ความจุ = rounduptopowerof2 (tosize); threshold = (int) math.min (ความจุ * loadfactor, maximum_capacity + 1); ตาราง = รายการใหม่ [ความจุ]; inithseedasneeded (ความสามารถ); -โดยที่ RoundupTopowerof2 คือการได้รับพลังงาน n มากกว่า 2 มากกว่าหรือเท่ากับพารามิเตอร์ที่กำหนด
INT คงที่ส่วนตัว RoundupTopowerof2 (หมายเลข int) {// ยืนยันหมายเลข> = 0: "จำนวนต้องไม่ลบ"; หมายเลขส่งคืน> = maximum_capacity? maximum_capacity: (หมายเลข> 1)? Integer.highestonebit ((หมายเลข - 1) << 1): 1; -Integer.hightestonebit (INT) เป็นการดำเนินการที่รักษาบิตสูงสุดของพารามิเตอร์ที่กำหนดและเปลี่ยน 0 ที่เหลืออยู่เพียงแค่ใส่มันคือการเปลี่ยนพารามิเตอร์ int เป็น n powers น้อยกว่าหรือเท่ากับสูงสุด 2
หากตัวเลขคือ N กำลัง 2 บิตสูงสุดจะอยู่ที่บิตสูงที่สองเดิมหลังจากลดลง 1 จากนั้นเลื่อน 1 บิตให้ยังคงอยู่ในตำแหน่งบิตสูงสุด หากตัวเลขไม่ใช่ N กำลัง 2 บิตสูงสุดยังคงเป็นบิตสูงสุดเดิมหลังจากลดลง 1 บิตไป 1 บิตเพื่อเลื่อน 1 บิตให้เหลืออยู่
ขยายกำลังการผลิต:
HashMap จะมีพฤติกรรมการปรับขนาดเมื่อดำเนินการ ซอร์สโค้ดเฉพาะมีดังนี้:
โมฆะปรับขนาด (int newCapacity) {entry [] oldTable = ตาราง; int oldcapacity = oldTable.length; // ตารางแฮชมีความจุสูงสุด 1 << 30 ถ้า (ความเป็นไปได้ == maximum_capacity) {threshold = integer.max_value; กลับ; } รายการ [] newTable = รายการใหม่ [newCapacity]; // โอนรายการใน oldtable ไปยัง newtable // ค่าผลตอบแทนของ inithseedasneeded กำหนดว่าจะคำนวณการถ่ายโอนค่าแฮชใหม่ (newtable, inithseedasneeded (newcapacity)); ตาราง = newTable; // การคำนวณเกณฑ์เกณฑ์ใหม่ = (int) math.min (newcapacity * loadfactor, maximum_capacity + 1); } โมฆะการถ่ายโอน (รายการ [] newTable, บูลีน rehash) {int newCapacity = newTable.length; // transweep oldtable สำหรับ (รายการ <k, v> e: ตาราง) {// ห่วงโซ่ความขัดแย้ง transweep ในขณะที่ (null! = e) {entry <k, v> next = e.next; ถ้า (rehash) {// คำนวณค่าแฮชใหม่ e.hash = null == e.key? 0: แฮช (e.key); } int i = indexfor (e.hash, newcapacity); // แทรกองค์ประกอบลงในหัววิธีการแทรกส่วนหัว e.next = newTable [i]; newTable [i] = e; e = ถัดไป; -ข้างต้นเป็นกระบวนการทั้งหมดของการจัดสรรหน่วยความจำ HashMap โดยสรุปเมื่อ HASHMAP วางรายการจะตรวจสอบความจุปัจจุบันและขนาดเกณฑ์เพื่อเลือกว่าจะขยายกำลังการผลิตหรือไม่ ขนาดของการขยายแต่ละครั้งคือ 2 * ตารางความยาว ในช่วงระยะเวลาการขยายตัวมันจะพิจารณาว่าค่าแฮชจะต้องคำนวณใหม่ตาม inithaseedasneeded หรือไม่
4. HashMap Iterator
ตัววนซ้ำเช่นผู้ประเมินราคาผู้พิทักษ์ผู้เข้าร่วมงานและอื่น ๆ ใน HASHMAP ทั้งหมดขึ้นอยู่กับ Hashiterator มาดูซอร์สโค้ดกันเถอะ:
คลาสนามธรรมส่วนตัว Hashiterator <E> ใช้ตัววนซ้ำ <e> {entry <k, v> next; // รายการถัดไปเพื่อส่งคืน int ที่คาดหวัง modcount; // สำหรับดัชนี int fail fail; // สล็อตปัจจุบัน, รายการดัชนีตาราง <k, v> ปัจจุบัน; // รายการปัจจุบัน hashiterator () {คาดว่า modcount = modcount; // ค้นหารายการแรกในตารางแฮชถ้า (ขนาด> 0) {รายการ [] t = ตาราง; ในขณะที่ (ดัชนี <t.length && (next = t [index ++]) == null); }} สาธารณะบูลีนสุดท้าย hasnext () {return next! = null; } รายการสุดท้าย <k, v> nextEntry () {// hashmap เป็นแบบไม่ปลอดภัยและเมื่อข้ามไปยังคงพิจารณาว่ามีการดัดแปลงโครงสร้างตารางหรือไม่ถ้า (modcount! รายการ <k, v> e = ถัดไป; ถ้า (e == null) โยน nosuchelementException ใหม่ (); if ((next = e.next) == null) {// ค้นหารายการถัดไป [] t = ตาราง; ในขณะที่ (ดัชนี <t.length && (next = t [index ++]) == null); } current = e; กลับ E; } โมฆะสาธารณะลบ () {ถ้า (current == null) โยน unlueLstateException ใหม่ (); if (modcount! = bostedModCount) โยนใหม่พร้อมกัน modificationException (); วัตถุ k = current.key; ปัจจุบัน = null; hashmap.his.removeentryforkey (k); คาดว่า ModCount = ModCount; }}ตัววนซ้ำทั้งสามคีย์ค่าและรายการถูกห่อหุ้มและกลายเป็นสามมุมมองการรวบรวม: คีย์, ค่าและชุดรายการ มุมมองการรวบรวมทั้งสามนี้รองรับการลบลบและการดำเนินงานที่ชัดเจนของ HashMap และไม่สนับสนุนการดำเนินการ Addal และ Addall