การทำงานของ HashMap
บักเก็ต การแฮช และการชนกัน
การทำงานของ HashMap เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
สิ่งที่ HashMap จัดเก็บ
HashMap จัดเก็บคู่คีย์-ค่า และให้การค้นหา การแทรก และการลบโดยเฉลี่ยที่มีประสิทธิภาพระดับ O(1)
ภายในจะเก็บอาร์เรย์ที่เรียกว่า ตาราง แต่ละตำแหน่งในอาร์เรย์นี้เรียกว่า ช่อง
- คีย์จะกำหนดว่ารายการจะไปอยู่ในช่องใด
- ค่าคือสิ่งที่คุณได้รับกลับมาเมื่อค้นหาด้วยคีย์
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 30);
ages.put("Bob", 25);
System.out.println(ages.get("Alice"));
}
}การทำแฮชของคีย์
เมื่อคุณเรียก put(key, value) แมปจะเรียก key.hashCode() เพื่อรับค่า int
จากนั้น HashMap จะ กระจาย บิตเหล่านั้นด้วยฟังก์ชันภายใน เพื่อให้แม้รหัสแฮชจะมีคุณภาพต่ำ ก็ยังกระจายไปตามช่องต่าง ๆ ได้
- ตัวเลขสุดท้ายจะถูกลดค่าด้วย
hash & (table.length - 1)เพื่อให้ได้ดัชนีของช่อง - ความยาวของตารางเป็นเลขยกกำลังของสองเสมอ ดังนั้นมาสก์จึงใช้งานได้
public class Main {
public static void main(String[] args) {
String key = "Alice";
int h = key.hashCode();
int spread = h ^ (h >>> 16);
int index = spread & (16 - 1);
System.out.println("hashCode: " + h);
System.out.println("bucket index: " + index);
}
}ช่องต่าง ๆ ในการทำงาน
แต่ละช่องสามารถเก็บรายการได้มากกว่าหนึ่งรายการ เมื่อคีย์สองรายการถูกแมปไปยัง ช่องเดียวกัน จะเรียกว่าเกิด การชนกัน
การชนกันเป็นเรื่องปกติและคาดหมายได้ HashMap จัดการโดยเชื่อมรายการต่าง ๆ เข้าด้วยกันภายในช่อง
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<Integer, String> m = new HashMap<>();
for (int i = 0; i < 5; i++) {
m.put(i, "v" + i);
}
System.out.println(m.size() + " entries stored");
}
}การชนกันและการเชื่อมโยง
ก่อน Java 8 รายการทั้งหมดที่ชนกันจะอยู่ใน รายการเชื่อมโยงทางเดียว ภายในช่อง
การค้นหาจะไล่ดูรายการโดยเรียก equals() จนกว่าจะพบคีย์ที่ตรงกัน
- การชนกันน้อย: ยังคงมีประสิทธิภาพโดยรวมเทียบเท่า O(1)
- การชนกันจำนวนมากในช่องเดียว: ประสิทธิภาพของช่องนั้นจะลดลงเข้าใกล้ O(n)
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<>();
m.put("FB", 1);
m.put("Ea", 2);
System.out.println("FB hash: " + "FB".hashCode());
System.out.println("Ea hash: " + "Ea".hashCode());
System.out.println(m.get("FB") + ", " + m.get("Ea"));
}
}เหตุใด FB และ Ea จึงชนกัน
สตริง "FB" และ "Ea" มีค่า hashCode() เท่ากันใน Java นี่คือตัวอย่างคลาสสิกของการชนกัน
แม้รหัสแฮชจะเหมือนกันทุกประการ แต่แมปก็ยังเก็บทั้งสองรายการแยกจากกันได้ เพราะ equals() ใช้แยกความแตกต่างภายในช่อง
public class Main {
public static void main(String[] args) {
System.out.println("FB".hashCode() == "Ea".hashCode());
System.out.println("FB".equals("Ea"));
}
}อัตราการใช้งาน
อัตราการใช้งานจะควบคุมว่าตารางจะเต็มเพียงใดก่อนขยายขนาด ค่าเริ่มต้นคือ 0.75
- ความจุ 16 และอัตราการใช้งาน 0.75 หมายความว่าการปรับขนาดจะเริ่มเมื่อมี 12 รายการ
- อัตราการใช้งานที่ต่ำกว่าจะใช้หน่วยความจำมากขึ้น แต่ลดการชนกัน
- อัตราการใช้งานที่สูงกว่าจะประหยัดหน่วยความจำ แต่เพิ่มการชนกัน
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<Integer, Integer> m = new HashMap<>(16, 0.75f);
for (int i = 0; i < 12; i++) m.put(i, i);
System.out.println("Stored " + m.size() + " entries");
}
}การปรับขนาดตาราง
เมื่อจำนวนรายการมากกว่า capacity * loadFactor ตารางจะมีขนาด เพิ่มเป็นสองเท่า
รายการเดิมทุกรายการจะถูก คำนวณแฮชใหม่ แล้วใส่ลงในตารางที่ใหญ่ขึ้น การดำเนินการนี้ใช้ทรัพยากรมาก ดังนั้นการกำหนดขนาดล่วงหน้าจึงสำคัญสำหรับแมปขนาดใหญ่
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// Pre-size to avoid repeated resizes
Map<Integer, Integer> m = new HashMap<>(1024);
for (int i = 0; i < 800; i++) m.put(i, i * 2);
System.out.println("size = " + m.size());
}
}การกำหนดขนาดล่วงหน้าเพื่อประสิทธิภาพ
หากคุณทราบคร่าว ๆ ว่าจะจัดเก็บรายการกี่รายการ ให้กำหนดความจุเริ่มต้นเพื่อหลีกเลี่ยงการปรับขนาดซ้ำ ๆ
หลักคร่าว ๆ คือ ความจุเริ่มต้น = expectedSize / 0.75 + 1
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
int expected = 1000;
int capacity = (int) (expected / 0.75) + 1;
Map<Integer, String> m = new HashMap<>(capacity);
System.out.println("Initial capacity hint: " + capacity);
m.put(1, "ok");
System.out.println(m.get(1));
}
}คีย์และค่าที่ไม่มีค่า
HashMap อนุญาตให้มี คีย์ที่มีค่าเป็นค่าว่างหนึ่งรายการ และ ค่าที่เป็นค่าว่างได้หลายรายการ
- คีย์ที่มีค่าเป็นค่าว่างจะไปอยู่ที่ช่อง 0 เสมอ (แฮชของคีย์นี้จะถือเป็น 0)
- ใช้
getOrDefaultเพื่อหลีกเลี่ยงความกำกวมระหว่างคีย์ที่ไม่มีอยู่กับค่าที่ไม่มีค่า
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, String> m = new HashMap<>();
m.put(null, "nullKeyValue");
m.put("a", null);
System.out.println(m.get(null));
System.out.println(m.getOrDefault("missing", "default"));
}
}ไม่รับประกันลำดับการวนซ้ำ
HashMap ไม่รับประกันลำดับการวนซ้ำ ลำดับจะขึ้นอยู่กับรหัสแฮชและโครงสร้างของช่อง
หากต้องการลำดับที่คาดเดาได้ ให้ใช้ LinkedHashMap (ลำดับการแทรก) หรือ TreeMap (ลำดับที่เรียงแล้ว)
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<>();
m.put("one", 1);
m.put("two", 2);
m.put("three", 3);
for (Map.Entry<String, Integer> e : m.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
}
}สรุปเส้นทาง get()
การค้นหาดำเนินตามขั้นตอนต่อไปนี้:
- คำนวณ
hashCode()และกระจายบิต - ใช้มาสก์เพื่อค้นหาดัชนีของช่อง
- ไล่ดูช่องโดยเปรียบเทียบคีย์ด้วย
equals() - ส่งคืนค่าที่ตรงกันหรือค่าว่าง
ค่า hashCode ที่ดีและ equals ที่ถูกต้องทำให้ทุกขั้นตอนรวดเร็ว
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> stock = new HashMap<>();
stock.put("apple", 50);
stock.put("pear", 20);
String key = "apple";
Integer qty = stock.get(key);
System.out.println(key + " -> " + qty);
}
}ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจเกี่ยวกับวิธีที่ HashMap ค้นหาช่อง
ทบทวน
คุณได้เรียนรู้การทำงานภายในของ HashMap ดังนี้:
- คีย์จะถูกคำนวณค่าแฮชและจัดสรรไปยังช่อง
- การชนกันจะถูกจัดการด้วยการเชื่อมรายการไว้ในช่อง
- อัตราการบรรจุ (0.75) จะกระตุ้นให้เพิ่มขนาดเป็นสองเท่าและคำนวณแฮชใหม่
- การกำหนดขนาดล่วงหน้าช่วยหลีกเลี่ยงการปรับขนาดที่มีต้นทุนสูง และไม่รับประกันลำดับการวนซ้ำ
ต่อไป เราจะดูว่าเหตุใด hashCode เพียงอย่างเดียวจึงไม่เพียงพอหากไม่มี equals ที่ถูกต้อง
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<>(64);
m.put("recap", 1);
System.out.println("HashMap basics complete: " + m.get("recap"));
}
}คำถามที่พบบ่อย
บทเรียน “การทำงานของ HashMap” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การทำงานของ HashMap” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การทำงานของ HashMap”
บักเก็ต การแฮช และการชนกัน คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “การทำงานของ HashMap” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การทำงานของ HashMap
- สัญญาของ equals/hashCode
- การสร้าง hashCode
- การเปลี่ยนเป็นต้นไม้และประสิทธิภาพ