Releases: jdereg/java-util
4.103.0
java-util 4.103.0
Maven Central: com.cedarsoftware:java-util:4.103.0
Highlights
New APIs
com.cedarsoftware.util.internal.VectorizedArrays— exposesequalsRange/mismatchRange/compareRangefor bothchar[]andbyte[]. Dispatches at runtime to JDK 9+ SIMD-vectorizedArrays.*intrinsics, with hand-rolled loop fallbacks for JDK 8. Resolution happens once at class load viaMethodHandles, so per-call cost is a static-field read +invokeExact. Internal package for now; promoted to public API once the contract stabilizes.Converter.convert(String, byte[].class)(and transitivelyString → ByteBuffer) performs multi-format detection — stringified JSON number arrays, spaced hex, unspaced hex (length ≥ 8, catchesCAFEBABE/DEADBEEFmagic numbers, compact UUIDs, SHA hashes), URL-safe Base64, then standard Base64. Tightness rules prevent short tokens like"DATA"/"ABCD"from being mis-classified and let them fall through to the historical charset path.MapConversions.toByteArray(and refactoredtoByteBuffer) handles the wrapped-form contract{"@type":"<type>","value":"<base64>"}. Strict Base64 decode first to preserve round-trip for short blobs; falls through to generalConverterdispatch on failure. Enables clean round-trip of the wrapped form thatJsonGenerator.writeBinaryemits in json-io 4.103.0.
Performance
- Cycle-tracking
IdentityHashMapallocation deferred to first-need acrossArrayConversions.arrayToArray/collectionToArrayandCollectionConversions.arrayToCollection/collectionToCollection— and skipped entirely for primitive-target conversions (primitive arrays can't form cycles). Profiling showed ~560 ms of accumulated allocation+put cost inarrayToArrayalone. ClassUtilities.newInstance()— cached constructor plans now fast-invoke non-varargs constructors when supplied arguments are already in constructor-parameter order and directly assignable, bypassing matcher allocations on repeated positional construction. No-arg cached construction reuses the empty argument-shape key and skips argument matching. Plan-cache entries retain constructor parameter metadata, avoiding repeatedConstructor.getParameters()calls.
Build
- Test-scope dependency bumps —
junit-jupiter5.14.3 → 5.14.4;agrona1.22.0 → 1.23.1 (still JDK 8 compatible). - Registered the JDK 9+ standard
@apiNote/@implSpec/@implNotetags withmaven-javadoc-pluginso the tool no longer emits "unknown tag" warnings.
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.102.0
java-util 4.102.0
Maven Central: com.cedarsoftware:java-util:4.102.0
Highlights
Performance
- Vendored Werner Randelshofer's FastDoubleParser (MIT, Java 8) under
com.cedarsoftware.util.fastdoubleparser. Eisel-Lemire algorithm forDouble/Float; recursive-multiplication forBigDecimal/BigInteger. 2-4× faster thanDouble.parseDouble; 5-50× faster thannew BigInteger(String)on 100+ digit values. Keeps java-util dependency-free (mirrors Jackson's vendoring choice). MathUtilities— added 20 fast number-parsing entry points (parseDouble/parseFloat/parseBigDecimal/parseBigInteger×CharSequence/char[]/char[]+offset+length/byte[]/byte[]+offset+length). Thechar[]/byte[]overloads avoid intermediateStringmaterialization.ConverterStringConversionsroutes through them, soConverter.convert(String, Double.class)and analogous calls pick up the speedup transparently.DateUtilities— two regex hot-path fixes: cached security-config getters now use avolatiletri-state instead ofSystem.getProperty(...)per parse (removes the synchronizedHashtablelock); per-Patternthread-localMatcherreuse instead ofpattern.matcher(input)per call. Together: ~2.7s self-time eliminated onJsonPerformanceTest.RegexUtilities.SafeMatchResult— replaced eager group extraction with a lazyMatchResultsnapshot. Unaccessed capturing groups no longer allocate aStringper call. ~840ms ofJsonPerformanceTestwall time recovered.FastReader— added borrowed-slice fast pathsreadUntilBorrowed(...)andreadLineBorrowed(...)for callers that can consume directly from the internal buffer. Existing delimiter semantics preserved; falls back cleanly to copying APIs when pushback is active or the token crosses a buffer boundary.
Correctness
FastReader.readUntilBorrowed()now clamps its scan boundary by available buffer length instead of computingpos + maxLen(which could overflow on very largemaxLenwith a non-zero buffer position).FastReader.BufferSlice— added assertion-checkedrelease()lifecycle for borrowed slices. Callers must release before the next read/pushback/close; outstanding borrows fail fast under-ea. Production behavior unchanged.
Build
- jackson-databind test dependency bumped 2.21.2 → 2.21.3 (test-scope only).
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.101.0
java-util 4.101.0
Maven Central: com.cedarsoftware:java-util:4.101.0
Highlights
New APIs
ClassValueMap.getByClass(Class)— typed fast-path lookup that skips theinstanceof Classguard inget(Object), compiling to a near-directClassValue.get(type)call. Converted 30+ hot-path call sites acrossClassUtilities,Converter.ClassPairMap,MultiKeyMap,TypeUtilities,Unsafe,CompactMap, andCollectionHandling.ClassValueSet.containsClass(Class)— parallel typed fast path on the Set side. Skips the null check andinstanceof Classguard incontains(Object). Converted 9 hot-path call sites inMultiKeyMap(flatten/hash/equals paths) andClassUtilities.SECURITY_CHECK_CACHE.ReflectionUtils.getDirectClassAnnotation(Class, Class)— strict JDK-semantics companion togetClassAnnotation. MatchesClass.getAnnotation(Class)exactly (respects@Inherited, never walks interfaces), for identity-tied annotations like@Entity,@JsonNaming,@IoNaming.StringUtilities.getChars(String, int)+getChars(String)— SIMD bulk-copy helpers for hot-pathcharAt-loop replacements.
Performance
Converter— refactored 4 internal caches (CONVERSION_DB,FULL_CONVERSION_CACHE,USER_DB,cacheInheritancePairs) fromMap<ConversionPair, V>to nestedClassValueMap<ClassValueMap<V>>, eliminating per-lookupConversionPairallocation from everyconvert()call.ConversionPairis gone from the JFR hot-path allocation top-5.Converter.USER_DB— dropped redundantinstanceIdfrom cache keys (legacy from an earlier static design).FastReader.readUntil/readLine— 30% reduction in GETFIELD field accesses on the common path (scan-loop local hoisting, elided unconditional PUTFIELD writes, refill moved to loop top).FastReader.readLine(char[], int, int)— ~22% self-time reduction via explicit CSE hoisting ofb[scanPos].StringUtilities— SIMD-intrinsicString.getChars()bulk-copy pattern applied tolevenshteinDistance,damerauLevenshteinDistance, anddecode(String)(hex). Loop-invariant chars hoisted out of the Levenshtein inner loops.
Correctness
ClassValueMap— markedcachedEntrySet,cachedValues,cachedUnmodifiableViewvolatileto close a latent DCL publication race.ReflectionUtils.getAllConstructorsInternal()— added deterministic tie-breaker to constructor sort (fixes ~30% intermittent failure rate inClassUtilitiesVarargsArrayStoreTestunder JaCoCo).
Security
ReflectionUtils.isTrustedCaller()— stack walk no longer misidentifies java-util's own internal cache infrastructure as a trusted caller. Previous prefix-match allowed external callers to bypass dangerous-class blocking on paths routed through cache-populating lambdas.
Build
- JaCoCo code coverage plugin wired in permanently. Reports auto-generated on every
mvn testattarget/site/jacoco/.
Testing
- 19,964 tests total — +380 new coverage tests across
EncryptionUtilities,ReflectionUtils,UrlUtilities,IOUtilities,DeepEquals,MultiKeyMap,ClassUtilities, plus newClassValueMapTestandClassValueSetTestentries for the typed fast paths.
Full changelog: https://github.com/jdereg/java-util/blob/master/changelog.md
4.100.0
What's Changed
-
BUG FIX: Fixed unbounded memory leak in
Converter.FULL_CONVERSION_CACHE. Each short-livedConverterinstance (e.g., one perJsonIo.toJava()call) cached inheritance-resolved conversions keyed by its uniqueinstanceIdin a staticConcurrentHashMap. When the instance was GC'd, its entries remained forever. In production under sustained load, this grew to 1GB+. Fix: cache resolved conversions at the shared level (instanceId=0L) when no user-added conversions exist; skip static caching otherwise. The cache is now bounded by the number of unique(source, target)type pairs, not by the number ofConverterinstances created. -
PERFORMANCE: Optimized
Converter.getCachedConverter()to skip the now-unnecessary instance-specific lookup, eliminating a wastedConversionPairallocation per call. -
Updated test dependencies: JUnit 5.14.2→5.14.3, Jackson 2.21.1→2.21.2
4.99.0
4.99.0 - 2026-03-28
- FEATURE: New
StringUtilities.getChars(String s)— public API that returns a ThreadLocalchar[]buffer populated viaString.getChars()(SIMD-optimized bulk copy). Callers can replacestr.charAt(i)loops with directbuf[i]array access. - PERFORMANCE:
StringUtilities.hashCodeIgnoreCase(String)— usesStringUtilities.getChars()into a ThreadLocalchar[]buffer, then hashes from the array directly. CaseInsensitiveMap GET improved 70-75%, PUT improved 48-52%, and MIXED-CASE GET improved 68% on 100K entries. - PERFORMANCE: New
FastReader.readLine(char[] dest, int off, int maxLen)— dedicated line-reading method optimized for TOON's line-oriented parsing. Combines scanning, copying, and line-ending consumption into a single call. JFR shows TOON line-reading improved 28%. - PERFORMANCE:
FastReader.readUntil()pushback drain loop now uses a local variable forpushbackPosition, avoiding load/store throughthison each iteration. JFR shows 14.8% reduction in FastReader CPU share. - PERFORMANCE:
FastReader.readUntil()replacedMath.min()with inline ternary in tight buffer-scan loop. JFR confirmed 3.5% wall-clock improvement. - PERFORMANCE:
FastReader.readUntil()now cachesthis.limitinto a local variable afterfill(). JFR showsreadUntilself-time dropped ~15%. - PERFORMANCE:
FastReader.fill()extracted core I/O intorefill()which uses a localint nfor thein.read()result, reducing PUTFIELD/GETFIELD overhead.
4.98.0
Changes
- PERFORMANCE:
CaseInsensitiveMap.get(),containsKey(), andremove()now use aThreadLocal<LookupKey>for String key lookups on hash-based backings (HashMap, LinkedHashMap, ConcurrentHashMap), eliminating per-callCaseInsensitiveStringallocation. This removes the single largest remaining allocation cost center (403 JFR samples).LookupKeyis a lightweight mutable object reused via ThreadLocal, with bidirectional equals support for both HashMap and ConcurrentHashMap lookup directions. SortedMap backings continue to useCaseInsensitiveStringsince they requireComparablekeys. - PERFORMANCE:
CaseInsensitiveMapnow overridessize()andisEmpty()to delegate directly to the backing map, bypassing theAbstractMap.size()→entrySet().size()indirection chain (168 JFR samples eliminated). - PERFORMANCE:
CaseInsensitiveMapinternal fields changed fromprivateto package-private to eliminate JVM synthetic accessor methods generated for anonymous inner class access (40 JFR samples eliminated). - PERFORMANCE:
StringUtilities.hashCodeIgnoreCase()now inlines the case-fold logic directly into the hash loop, ensuring C2 JIT compiles the entire loop as a single compilation unit. - PERFORMANCE:
StringUtilities.foldCaseForHash()branch ordering optimized to checkc <= 'Z'first, partitioning ASCII so uppercase and lowercase each take only two comparisons. - BUG FIX:
ConverterDurationToOffsetDateTimeTestused the current system timezone offset to compute expected values, but the conversion target is the epoch (1970). Fixed to compute the offset at the actual target instant. - TESTING: Added
ToonRoundTripTest— 46 parameterized tests verifying allConverter-supported types round-trip through TOON format. - TESTING: Added
ConverterDurationToOffsetDateTimeTest— verifies Duration → OffsetDateTime conversion across multiple scenarios.
Maven:
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>java-util</artifactId>
<version>4.98.0</version>
</dependency>4.97.0
Changes
- PERFORMANCE:
FastReader.readUntil()now splits the inner loop into a read-only delimiter scan followed by a bulkSystem.arraycopy, allowing the JIT to optimize the tight scan loop independently from memory writes. - PERFORMANCE:
FastReader.readUntil()scan loop now uses ado-whilewith a single array access per iteration and hoists position assignment above the delimiter check to eliminate a duplicate write.
Maven:
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>java-util</artifactId>
<version>4.97.0</version>
</dependency>4.96.0
Bug Fix
ClassUtilities.trySetAccessible()no longer caches successfulsetAccessible(true)results. TheWeakHashMap-based cache usesequals()for lookup, butField.equals()matches by declaring class, name, and type — not identity. WhengetDeclaredFields()was called with different predicates, the JVM returned differentFieldinstances for the same logical field; the cache returnedTRUEfor the second instance without ever callingsetAccessible(true)on it, leaving it inaccessible. This causedTraverserto silently skip inaccessible fields, breakingGraphComparator.applyDelta(). Only failures (FALSE) are now cached to avoid expensive repeated exceptions on JPMS-sealed modules.
Install
Maven
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>java-util</artifactId>
<version>4.96.0</version>
</dependency>Gradle
implementation 'com.cedarsoftware:java-util:4.96.0'