Skip to content

Commit a28b59b

Browse files
jderegclaude
andcommitted
Performance: Remove CaseInsensitiveString LRU cache, construct directly in hot path
The global LRU cache (ConcurrentHashMap + linked-list bookkeeping) added ~60-100ns overhead per map operation to cache objects that cost ~20-30ns to construct. Bypassing the cache makes CaseInsensitiveMap(HashMap) 4.86x faster than TreeMap(CASE_INSENSITIVE_ORDER), up from 8.77x slower (~43x relative improvement). - convertKey()/convertKeys() now use new CaseInsensitiveString() directly - Fixed equals() bug: hash==0 guard skipped equalsIgnoreCase() comparison - Removed cache infrastructure (COMMON_STRINGS_REF, trimCache, etc.) - Deprecated replaceCache(), resetCacheToDefault(), setMaxCacheLengthString() as no-ops for backwards compatibility - Deleted CaseInsensitiveCacheContentionTest and CacheTypeComparisonTest - Updated remaining tests to use assertEquals instead of assertSame Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 294ce27 commit a28b59b

6 files changed

Lines changed: 73 additions & 1237 deletions

File tree

changelog.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
### Revision History
22

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
421
* **BUG FIX**: `DeepEquals` - Enum constants with class bodies misclassified as `TYPE_MISMATCH`
522
* `Class.isEnum()` returns `false` for anonymous subclasses created by enum constants with bodies (e.g., `FOO { @Override ... }`)
623
* 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,7 +289,7 @@
272289
* **PERFORMANCE**: `CompactMap` - Cache size in iterator
273290
* Replaced `size()` calls in `hasNext()` and `advance()` with the already-tracked `expectedSize` field, avoiding `instanceof` dispatch on every iteration step
274291

275-
#### 4.90.0 2026-02-02
292+
#### 4.90.0 - 2026-02-02
276293
* **BUG FIX**: `DeepEquals` - URL comparison now uses string representation instead of `URL.equals()`
277294
* Java's `URL.equals()` performs DNS resolution which causes flaky CI failures
278295
* Now compares URLs using `toExternalForm()` for reliable, deterministic comparison

0 commit comments

Comments
 (0)