นากกายกำลังแขวนกุญแจหน้าตาเหมือนกันสองดอกบนตะขอคนละอัน

Java IdentityHashMap: เมื่อ key ต้องเป็น object เดิม ไม่ใช่ object ที่ equals กัน

พี่โชว์Engineering

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