A HashMap stores key-value associations using hashing. Its key-related failure mode is easy to miss: changing a key after insertion can make the entry impossible to find or remove. section 8.0 section 8.6
A mutable key becomes unreachable
A list is inserted as a set element and then modified. Its hash code changes, but the object remains in the bucket selected by its original hash code. A later contains or remove operation uses the new hash code, searches another bucket, and fails. section 8.6
Avoid mutable or equality-inconsistent keys:
- Do not modify state affecting equals() or hashCode() while an object serves as a map key. section 8.6
- Prefer immutable key types; LocalDateTime is identified as a modern alternative to mutable Date and Calendar objects. section 8.6
- Treat StringBuilder as a risky key: its content-based compareTo() conflicts with identity-based equals() and hashCode(). section 8.6
- Use static analysis as an aid, not a guarantee; a missed copy of a mutable BitSet caused items to disappear and produced incorrect warnings. section 8.6
Set<List<String>> set = new HashSet<>();
List<String> list = new ArrayList<>();
set.add(list);
list.add("Value");
System.out.println(set.contains(list)); #1
Inspect the minimal mutable-key example and observe that changing the list after insertion affects lookup behavior. section 8.6
public static void main(String[] args) {
Set<StringBuilder> set = new HashSet<>();
for (int count = 0; count < 40; count++) { #1
set.add(firstBucketStringBuilder());
}
int[] hashCodes = set.stream() #2
.mapToInt(Object::hashCode)
.sorted().toArray();
StringBuilder sb =
Stream.generate(StringBuilder::new)
.filter(b -> Arrays.binarySearch(
hashCodes, b.hashCode()) >= 0)
.findFirst().get();
set.add(sb); #3
System.out.println(set.contains(sb)); #4
sb.append("b"); #5
System.out.println(set.contains(sb)); #6
}
static StringBuilder firstBucketStringBuilder() {
while (true) {
StringBuilder sb = new StringBuilder("a");
int hc = sb.hashCode(); #7
if (((hc ^ (hc >>> 16)) & 0x3F) == 0) {
return sb;
}
}
}
Explore the StringBuilder case, including hash collisions, insertion, mutation, and subsequent contains checks. section 8.6
java -XX:+UnlockExperimentalVMOptions -XX:hashCode=2
Compare behavior under the configured hashCode option when investigating implementation-sensitive HashMap behavior. section 8.6









































