CAMEL-23691: Improve CaseInsensitiveMap with O(1) hash table and header key deduplication#23766
Conversation
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
|
🧪 CI tested the following changed modules:
Build reactor — dependencies compiled but only changed modules were tested (5 modules)
|
…er key deduplication Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Claude Code on behalf of Guillaume Nodet Addressed review feedback: replaced reflection with |
davsclaus
left a comment
There was a problem hiding this comment.
Solid replacement for the TreeMap-based CaseInsensitiveMap — O(1) lookups, zero-allocation hash, and key deduplication via ExchangeConstantProvider (no reflection). The hash function correctly uses the two-step Unicode fold matching String.equalsIgnoreCase(). 10 new tests cover insertion order, iterator removal, resize, null values, and key dedup.
Minor observations (non-blocking):
- Import ordering in
constant-provider.vm:java.util.Collectionis placed afterjavax.annotation.processing.Generated. The build formatter fixes the generated file, but the template itself has the wrong order. MapEntry.equals()uses case-sensitive key comparison (perMap.Entrycontract), while the map is case-insensitive. Only matters for direct entry comparison —EntrySet.contains()/remove()correctly use case-insensitivefindIndex().- After many put/remove cycles without resize, tombstoned slots accumulate in the entry arrays. Fine for typical short-lived Exchange headers, but worth noting for long-lived maps with high churn.
This review does not replace specialized AI review tools (CodeRabbit, Sourcery) or static analysis (SonarCloud).
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
We can deprecate camel-headersmap |
CAMEL-23691
Summary
Replace the
TreeMap-basedCaseInsensitiveMapwith a custom hash table implementation, improving the performance of every header access in every Camel route.CaseInsensitiveMapis the default headers map for everyExchangein Camel. It is the most accessed data structure in the framework — everymessage.getHeader(),setHeader(),removeHeader(), and header iteration goes through it.Before (TreeMap with
CASE_INSENSITIVE_ORDER)get,put,containsKey,removeStringobjects across exchangescamel-headersmapdependency (cedarsoftwarejava-util, which usesThreadLocalinternally — problematic with virtual threads)After (custom hash table)
get,put,containsKey,removeCharacter.toLowerCase(Character.toUpperCase(c)), keys compared withequalsIgnoreCase(). NotoLowerCase()string copiesExchangeheader constants (151 keys) are registered at startup viaExchangeConstantProvider; deserialized keys matching a known constant are replaced with the canonical interned reference, reducing memory across exchangesThreadLocal, no reflectioncamel-headersmapin most casesDesign
The map extends
AbstractMapand implements its own hash table with separate chaining using parallel arrays (keys[],values[],chainNext[]). This gives cache-friendly storage and avoids per-entry object allocation overhead ofHashMap.Node.The hash function uses the same two-step fold as
String.equalsIgnoreCase()—Character.toLowerCase(Character.toUpperCase(c))— ensuring hash consistency even for Unicode edge cases (e.g., Turkish dotless-ı).DefaultHeadersMapFactoryregisters allExchangestring constants at startup viaExchangeConstantProvider.values()→CaseInsensitiveMap.registerKnownKeys(). Onput(), a zero-allocation lookup in the static known-keys table replaces deserialized keys with canonical references.Compatibility
Map<String, Object>API is unchangedDefaultHeadersMapFactoryandinstanceof CaseInsensitiveMapchecks work as beforeSortedMap/NavigableMapmethods on this mapTest plan
CaseInsensitiveMapTest— 32 tests (+ 10 new: insertion order, iterator removal, entry setValue, entrySet remove, remove-then-reput, resize/rehash, containsValue, null values, clear-reuse, known key deduplication)DefaultMessageHeaderTest— 16 tests