equals-hashcode: equals/hashCode contract demos, missing hashCode and constant-hash costs, mutable keys, records, JPA entity id trap, 13 contract tests
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b66uwsWr9B1ouyrzUh
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
== Reflexive / null ==
|
||||
p.equals(p) = true
|
||||
p.equals(null) = false
|
||||
|
||||
== Symmetry broken by instanceof + extra field ==
|
||||
plain.equals(red) = true
|
||||
red.equals(plain) = false
|
||||
List.of(plain).contains(red) = false
|
||||
List.of(red).contains(plain) = true
|
||||
|
||||
== Transitivity broken by the lenient fix ==
|
||||
r.equals(plain) = true
|
||||
plain.equals(b) = true
|
||||
r.equals(b) = false
|
||||
|
||||
== Consistency broken by a changing equals() ==
|
||||
f.equals(f2) x4 = true false true false
|
||||
@@ -0,0 +1,15 @@
|
||||
== equals() without hashCode() ==
|
||||
a.equals(b) = true
|
||||
a.hashCode() == b.hashCode() = false
|
||||
HashSet size after adding a then b = 2
|
||||
HashSet.contains(new equal key) = false
|
||||
HashMap.get(equal key) = null
|
||||
|
||||
== with hashCode() ==
|
||||
HashSet size after adding c then d = 1
|
||||
HashSet.contains(new equal key) = true
|
||||
|
||||
== a legal but useless hashCode(): return 42 ==
|
||||
keys = 2000
|
||||
equals() calls for 2000 gets, constant hash = 2002999
|
||||
equals() calls for 2000 gets, good hash = 2000
|
||||
@@ -0,0 +1,14 @@
|
||||
before: get = 100, contains = true
|
||||
|
||||
after acc.owner = "ankur":
|
||||
map.get(acc) = null
|
||||
set.contains(acc) = false
|
||||
map.containsKey(acc) = false
|
||||
map.size() = 1
|
||||
iteration still sees it = [Account(ankur)]
|
||||
map.remove(acc) = null
|
||||
map.size() after remove = 1
|
||||
|
||||
set.add(acc) again -> size = 2 [Account(ankur), Account(ankur)] (one object, two entries)
|
||||
|
||||
immutable key: map.get("asha") = 1
|
||||
@@ -0,0 +1,3 @@
|
||||
date.equals(ts) = true
|
||||
ts.equals(date) = false
|
||||
date.getTime() == ts.getTime() = true
|
||||
@@ -0,0 +1,23 @@
|
||||
== generated equals/hashCode ==
|
||||
a.equals(b) = true
|
||||
a.hashCode() == b.hashCode() = true
|
||||
new HashSet of both, size = 1
|
||||
a == b = false
|
||||
|
||||
== array component: reference equality ==
|
||||
w1.equals(w2) = false
|
||||
same array shared: true
|
||||
|
||||
== mutable List component: equals works, hashCode can change ==
|
||||
hashCode before = 4714, after list.add(3) = 34537
|
||||
record in HashSet, list mutated -> contains = false
|
||||
|
||||
== doubles compare with Double.compare semantics ==
|
||||
NaN record equals NaN record = true
|
||||
Double.NaN == Double.NaN = false
|
||||
0.0 record equals -0.0 record = false
|
||||
0.0 == -0.0 = true
|
||||
|
||||
== overriding equals in a record ==
|
||||
t1.equals(t2) = true
|
||||
toString still shows both fields: Tagged[isbn=1, title=Effective Java 3e]
|
||||
@@ -0,0 +1,10 @@
|
||||
== id-based equals/hashCode, id assigned on save ==
|
||||
two unsaved entities equal? true (both ids null)
|
||||
set size after adding two different unsaved entities = 1
|
||||
add, then save(): set.contains(c) = false
|
||||
|
||||
== constant hashCode ==
|
||||
add, then save(): set.contains(d) = true
|
||||
|
||||
== natural key, immutable ==
|
||||
add, then save(): set.contains(e) = true
|
||||
@@ -0,0 +1,22 @@
|
||||
== what the JDK uses ==
|
||||
"hello".hashCode() = 99162322
|
||||
31*h + c over "hello" = 99162322
|
||||
List.of(1,2,3).hashCode() = 30817
|
||||
Arrays.hashCode({1,2,3}) = 30817
|
||||
Objects.hash(1,2,3) = 30817
|
||||
Set.of(1,2,3).hashCode() = 6 (sum of elements)
|
||||
Map.of(1,2).hashCode() = 3 (key^value per entry, summed)
|
||||
|
||||
== Objects.hash(...) traps ==
|
||||
Objects.hash("a") vs "a".hashCode() = 128 vs 97
|
||||
Objects.hash(1, 2) == Objects.hash(2, 1) ? false
|
||||
Objects.hash() (no args) = 1
|
||||
Objects.hash((Object) null) = 31
|
||||
|
||||
== Arrays as fields ==
|
||||
x.equals(y) = false, Arrays.equals = true
|
||||
x.hashCode() == y.hashCode() ? false
|
||||
Arrays.hashCode equal ? true
|
||||
|
||||
== equal hashCodes do not mean equal objects ==
|
||||
"Aa".hashCode() = 2112, "BB".hashCode() = 2112, equal strings? false
|
||||
@@ -0,0 +1,3 @@
|
||||
[INFO] Tests run: 13, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.205 s -- in com.ankurm.equalshashcode.EqualsHashCodeTest
|
||||
[INFO] Tests run: 13, Failures: 0, Errors: 0, Skipped: 0
|
||||
[INFO] BUILD SUCCESS
|
||||
Reference in New Issue
Block a user