|
1 | 1 | ### Revision History |
2 | 2 |
|
3 | | -#### 4.91.0 |
| 3 | +#### 4.92.0 (unreleased) |
| 4 | +* **PERFORMANCE**: `CaseInsensitiveMap` - Eliminated global LRU cache bottleneck in `convertKey()` hot path |
| 5 | + * Every `get()`/`put()`/`containsKey()` call went through `CaseInsensitiveString.of()`, which performed a `ConcurrentHashMap.get()` + LRU bookkeeping (node promotion, timestamp update) on a shared global cache — more expensive than the thing it cached |
| 6 | + * `CaseInsensitiveString` is a lightweight wrapper (String reference + pre-computed hash, ~20-30ns to construct) while the cache lookup cost ~60-100ns per operation |
| 7 | + * Fixed by constructing `CaseInsensitiveString` directly in `convertKey()` and `convertKeys()`, bypassing the cache entirely |
| 8 | + * **Result**: CaseInsensitiveMap(HashMap) went from 8.77x slower than TreeMap(CASE_INSENSITIVE_ORDER) to 4.86x faster (~43x relative improvement) |
| 9 | +* **BUG FIX**: `CaseInsensitiveString.equals()` - Hash code of 0 skipped string comparison |
| 10 | + * The guard `(hash == 0 || hash == cis.hash)` treated 0 as "uncomputed" and bypassed the hash comparison, but the hash is eagerly computed in the constructor and 0 is a valid hash value |
| 11 | + * Two different strings that both hash to 0 would incorrectly compare as equal (skipping `equalsIgnoreCase`) |
| 12 | + * Fixed by simplifying to `hash == cis.hash` — equal objects must have equal hashes |
| 13 | +* **CLEANUP**: `CaseInsensitiveMap` - Removed `CaseInsensitiveString` global cache infrastructure |
| 14 | + * The LRU cache (5,000-entry `ConcurrentHashMap` + linked-list nodes + background cleanup thread) added memory overhead and contention across all `CaseInsensitiveMap` instances with no performance benefit |
| 15 | + * Removed: `COMMON_STRINGS_REF`, `DEFAULT_CACHE_SIZE`, `DEFAULT_MAX_STRING_LENGTH`, `trimCache()`, `parseIntSafely()`, static initializer, probabilistic caching logic |
| 16 | + * `CaseInsensitiveString.of()` now delegates directly to `new CaseInsensitiveString(s)` |
| 17 | + * Deprecated as no-ops (for backwards compatibility): `replaceCache()`, `resetCacheToDefault()`, `setMaxCacheLengthString()` |
| 18 | + * System properties `caseinsensitive.cache.size` and `caseinsensitive.max.string.length` no longer have any effect |
| 19 | + |
| 20 | +#### 4.91.0 - 2026-02-08 |
4 | 21 | * **BUG FIX**: `DeepEquals` - Enum constants with class bodies misclassified as `TYPE_MISMATCH` |
5 | 22 | * `Class.isEnum()` returns `false` for anonymous subclasses created by enum constants with bodies (e.g., `FOO { @Override ... }`) |
6 | 23 | * Two different enum constants from the same enum with bodies bypassed the enum reference-equality check and fell through to the class-equality check, producing `TYPE_MISMATCH` instead of `VALUE_MISMATCH` |
|
272 | 289 | * **PERFORMANCE**: `CompactMap` - Cache size in iterator |
273 | 290 | * Replaced `size()` calls in `hasNext()` and `advance()` with the already-tracked `expectedSize` field, avoiding `instanceof` dispatch on every iteration step |
274 | 291 |
|
275 | | -#### 4.90.0 2026-02-02 |
| 292 | +#### 4.90.0 - 2026-02-02 |
276 | 293 | * **BUG FIX**: `DeepEquals` - URL comparison now uses string representation instead of `URL.equals()` |
277 | 294 | * Java's `URL.equals()` performs DNS resolution which causes flaky CI failures |
278 | 295 | * Now compares URLs using `toExternalForm()` for reliable, deterministic comparison |
|
0 commit comments