Strings Just Got Faster
StableValue, Records, and Value Classes
- Main confusion: how
StableValuediffers from records and upcoming value classes, all of which carry immutable data. - Clarifications:
StableValueis about lazy, exactly-once initialization of a field whose value is then guaranteed not to change.- Records/value classes are about what the data is (transparent, immutable, possibly identity‑less), not when it’s initialized.
- You can put a record or value class inside a
StableValue; they solve different problems.
- Compared with Kotlin’s
lateinit: Kotlin simulates this at the language level;StableValueexpresses a promise directly to the JVM, allowing stronger JIT optimizations.
@Stable, Final, and Constant Folding
finalis an access modifier and historically could be broken via reflection, so JIT often can’t fully trust it.@Stable(internal) and user-levelStableValue<T>explicitly promise the JVM that, once written, the value will never change.- This enables aggressive constant folding and propagation, even through layers of indirection and lazy initialization.
- Java records now forbid reflective mutation of final fields; longer term the JVM may “trust” more finals by default.
String.hashCode and Immutable Maps
String.hashCode()has long cached its result in a field; the change is that this hash field is now treated as stable.- For constant
Stringkeys in immutable maps (e.g.,Map.of), the JVM can:- Inline hash computation and bucket index.
- Potentially eliminate the map lookup entirely and substitute the value directly.
- Discussion explores how far this can go in the presence of collisions; consensus: constant folding can still significantly simplify immutable map access, though details depend on implementation.
Security and Hash Randomization Debate
- Some are surprised Java’s string hash isn’t randomized, given hash-flooding DoS attacks.
- Others argue:
- Java mitigates collision attacks in
HashMapvia tree-based buckets and input limits in frameworks. - Changing the hash contract or algorithm now would break compatibility.
- If you need collision-resistance, you should use specialized collections or hashes.
- Java mitigates collision attacks in
Performance Impact and Scope
- Questions about real-world gains: likely small per-service, but may matter at scale and in string-heavy paths (HTTP headers, maps keyed by strings).
- Kotlin/Scala and other JVM languages benefit automatically, as they use
java.lang.String. - Some skepticism about the example in the article (map of
Stringto native calls), but others note optimizers must handle “bad” or non-hand-tuned code too.
API Design and Ergonomics
- Some like the power of
StableValuebut find the API names (orElseGet) and wrapper-style usage awkward. - Suggestions appear for more concise language-level syntax for lazy stable fields; JEP currently favours a library/JVM mechanism over new syntax.