-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIdentityHashMapDemo.java
More file actions
142 lines (126 loc) · 5.52 KB
/
Copy pathIdentityHashMapDemo.java
File metadata and controls
142 lines (126 loc) · 5.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package Phase5_CollectionsLambdasStreams.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* java.util.IdentityHashMap<K, V> - Map That Compares With `==`
* -------------------------------------------------------------
* IdentityHashMap is just like HashMap except that it uses REFERENCE
* EQUALITY for keys (the `==` operator) and System.identityHashCode for
* hashing instead of equals/hashCode.
* <p>
*
* HashMap : `a.equals(b)` means same key
* IdentityHashMap : `a == b` means same key
* <p>
*
* Why It Exists
* -------------
* The standard Map contract says "use equals", but sometimes you really
* want OBJECT IDENTITY. Two main use cases:
* <p>
*
* 1. Graph traversal where each NODE OBJECT must be visited exactly
* once, even if two nodes happen to be equals().
* e.g. detecting cycles when serialising an object graph.
* <p>
*
* 2. Storing PER-OBJECT metadata where you specifically care that two
* different instances have separate entries even if they have the
* same contents.
* <p>
*
* When To Use It
* --------------
* - Implementing equals on a recursive data structure - track visited
* nodes by IDENTITY to avoid infinite loops on equal-but-distinct
* objects.
* - Building serialisers that must remember which instances they have
* already written, regardless of contents.
* - Caches keyed by the precise instance you were given.
* <p>
*
* Why You Should Normally Avoid It
* --------------------------------
* The vast majority of map use cases want VALUE equality. Using
* IdentityHashMap when you really wanted HashMap silently produces wrong
* lookups - two strings with the same content but different identities
* will be treated as different keys.
* <p>
*
* Other Notes
* -----------
* - Initial capacity refers to MAX EXPECTED ENTRIES, not buckets.
* IdentityHashMap uses linear probing in a flat array.
* - Iteration order is NOT defined.
* - Implements Map but DELIBERATELY violates the Map general contract
* for equals/hashCode - the Javadoc says so up front.
* - Not thread-safe.
*/
public class IdentityHashMapDemo {
public static void main(String[] args) {
section("1) HashMap vs IdentityHashMap - the headline difference");
String s1 = new String("hello"); // two equal but DIFFERENT objects
String s2 = new String("hello");
System.out.println("s1 == s2 = " + (s1 == s2)); // false - distinct
System.out.println("s1.equals(s2)= " + s1.equals(s2)); // true - same content
Map<String, Integer> normal = new HashMap<>();
Map<String, Integer> identity = new IdentityHashMap<>();
normal.put(s1, 1);
normal.put(s2, 2); // overrides - "equals" the same key
System.out.println("HashMap size = " + normal.size()); // 1
identity.put(s1, 1);
identity.put(s2, 2); // distinct instances - both kept
System.out.println("IdentityHashMap size = " + identity.size()); // 2
section("2) Same identity instance overrides as expected");
identity.put(s1, 99); // SAME instance again
System.out.println("after put(s1, 99) -> get(s1) = " + identity.get(s1));
System.out.println(" -> get(s2) = " + identity.get(s2));
section("3) Use case - graph cycle detection");
// We walk a small graph and skip nodes we've already seen.
// Two Node objects with the same fields would be `equals` in many
// implementations - we use IDENTITY to avoid skipping legitimately
// distinct nodes.
Node a = new Node("a");
Node b = new Node("b");
Node c = new Node("c");
a.next = b; b.next = c; c.next = a; // CYCLE!
Map<Node, Boolean> visited = new IdentityHashMap<>();
Node cur = a;
int steps = 0;
while (cur != null && !visited.containsKey(cur)) {
visited.put(cur, true);
System.out.println("visited " + cur.label);
cur = cur.next;
steps++;
}
System.out.println("stopped after " + steps + " steps, " +
visited.size() + " unique nodes");
section("4) Identity-based serialiser dedup");
// Imagine writing a structure to JSON; record EACH unique instance
// by identity so we can emit $ref={id} for repeats.
IdentityHashMap<Object, Integer> ids = new IdentityHashMap<>();
Object o1 = new Object();
Object o2 = new Object();
ids.put(o1, 1);
ids.put(o2, 2);
// Even if a third object would .equals() o1, it would get its own id:
Object o1Twin = new Object();
ids.put(o1Twin, 3);
System.out.println("ids size = " + ids.size());
section("5) Watch out - many map operations behave by IDENTITY here");
// containsKey, get, remove all use ==
System.out.println("contains s1 (instance) = " + identity.containsKey(s1));
System.out.println("contains \"hello\" lit = " + identity.containsKey("hello"));
// The literal "hello" is a third, distinct object - and identity-equality fails.
// OUTPUT (representative; HashMap ordering may vary)
}
static class Node {
final String label;
Node next;
Node(String l) { this.label = l; }
}
private static void section(String title) {
System.out.println("\n====== " + title + " ======");
}
}