Java IdentityHashMap: เมื่อ key ต้องเป็น object เดิม ไม่ใช่ object ที่ equals กัน
Map ปกติถือ key สองตัวเป็นตัวเดียวเมื่อ equals() คืน true แต่เครื่องมือเดิน object graph บางแบบต้องรู้ว่าเป็น reference เดิมจริงหรือไม่ครับ IdentityHashMap ใช้ == กับ key และเหมาะกับ topology-preserving transformation หรือ visited set ที่แยก object แม้ค่าข้างในเท่ากัน
WHY: ความเท่ากันของโดเมนกับตัวตนในหน่วยความจำไม่เหมือนกัน
String สองก้อนอาจมีข้อความเท่ากันแต่เป็น object คนละตัว HashMap รวมเป็น key เดียว ส่วน IdentityHashMap เก็บสองรายการ เหมือนกุญแจหน้าตาเหมือนกันแต่แขวนบนตะขอคนละบ้าน การเลือก map นี้จึงต้องมาจาก contract ไม่ใช่หวังแก้ bug ของ equals แบบลัด
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
String first = new String("same");
String second = new String("same");
assert first.equals(second);
assert first != second;
Map<String, Integer> byValue = new HashMap<>();
byValue.put(first, 1);
byValue.put(second, 2);
assert byValue.size() == 1;
Map<String, Integer> byIdentity = new IdentityHashMap<>();
byIdentity.put(first, 1);
byIdentity.put(second, 2);
assert byIdentity.size() == 2;
assert byIdentity.get(new String("same")) == null;
assert byIdentity.get(first) == 1;
}
}
ใช้ให้แคบ
กรณีเหมาะคือ serializer ที่ต้องรักษา shared references, deep-copy ที่ต้องป้องกัน cycle และ debugger/proxy table ที่ object identity คือข้อมูลจริง ห้ามใช้กับ user ID, product code, URL หรือ value object เพราะ lookup ด้วย object ใหม่ที่ค่าเท่ากันจะไม่เจอ ทำให้ behavior ขึ้นกับการ reuse instance ที่มองไม่เห็นจากข้อมูล
Map นี้ไม่ thread-safe, iteration order ไม่รับประกัน และจงใจละเมิด contract ทั่วไปของ Map ที่อิง equals จึงไม่ควรส่งออกผ่าน API กว้าง ๆ ห่อไว้หลังชื่อชนิดหรือ helper ที่บอกว่า identity สำคัญจะลดโอกาส misuse
Recap
- ใช้เมื่อ reference identity (
==) เป็นส่วนของโจทย์จริง - เหมาะกับ graph traversal, copy และ topology mapping
- ห้ามใช้แทน HashMap สำหรับ business/value keys
- ซ่อน implementation ไว้หลัง API ที่อธิบาย semantics