changelog.md
May 25, 2026 · View on GitHub
Revision History
4.104.0 - (Unreleased)
4.103.0 - 2026-05-25
- BUILD: Test-scope dependency bumps —
junit-jupiter5.14.3 → 5.14.4;agrona1.22.0 → 1.23.1 (still JDK 8 compatible; agrona 2.x dropped Java 8). - BUILD: Registered the JDK 9+ standard
@apiNote/@implSpec/@implNotetags withmaven-javadoc-pluginso the javadoc tool no longer emits "unknown tag" warnings for these. - FEATURE: New
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. 21 new tests. - FEATURE:
Converter.convert(String, byte[].class)(and transitivelyString → ByteBuffer) performs multi-format detection — tries 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 (length, padding, alphabet symbols) prevent short tokens like"DATA"/"ABCD"from being mis-classified and let them fall through to the historical charset path. - FEATURE: New
MapConversions.toByteArray(and refactoredtoByteBuffer) handles the wrapped-form contract{"@type":"<type>","value":"<base64>"}. Attempts 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. - PERFORMANCE:
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. - PERFORMANCE:
ClassUtilities.newInstance()no-arg cached construction reuses the empty argument-shape key, skips argument-matching on cached zero-parameter plans, and allocates the circular-referenceIdentitySetonly for inner-class recursion. - PERFORMANCE:
ClassUtilities.newInstance()constructor-plan cache entries retain constructor parameter metadata, avoiding repeatedConstructor.getParameters()calls on cached construction paths. - TESTING: 80 new
ConverterEverythingTestfixtures cover the format-detection ladder acrossString → byte[]/String → ByteBuffer/Map → byte[]/Map → ByteBuffer, including the documented charset-fallback cases to pin the tight-rule boundary.
4.102.0 - 2026-05-04
- BUILD: jackson-databind test dependency bumped 2.21.2 → 2.21.3. Test-scope only.
- 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, returningCOPY_REQUIREDeven when the delimiter was already in the buffer). - CORRECTNESS:
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. - PERFORMANCE:
RegexUtilities.SafeMatchResult— replaced eager group extraction with a lazyMatchResultsnapshot. Unaccessed capturing groups no longer allocate aStringper call; forDateUtilities.alphaMonthPattern(11 groups, 3-4 typically read) that's 7-8 wasted allocations per match. ~840ms ofJsonPerformanceTestwall time recovered. - PERFORMANCE:
DateUtilities— two regex hot-path fixes: (1) cached security-config getters use avolatiletri-state instead ofSystem.getProperty(...)per parse (removes the synchronizedHashtablelock); (2) per-Patternthread-localMatcherreuse viaConcurrentMap<Pattern, ThreadLocal<Matcher>>instead ofpattern.matcher(input)per call. Together: ~2.7s self-time eliminated onJsonPerformanceTest. - PERFORMANCE:
MathUtilities— added 20 fast number-parsing entry points (parseDouble/parseFloat/parseBigDecimal/parseBigInteger×CharSequence/char[]/char[]+offset+length/byte[]/byte[]+offset+length). Thechar[]andbyte[]overloads avoid intermediateStringmaterialization.ConverterStringConversionsroutes through them, soConverter.convert(String, Double.class)and analogous calls pick up the speedup transparently. - 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). Jar size 736 KB → 849 KB. - PERFORMANCE:
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. JFR:readUntilcopy samples 101→1 (toJava) / 76→4 (toMaps);readLine83→0 / 78→0.
4.101.0 - 2026-04-19
- BUG FIX:
ReflectionUtils.getAllConstructorsInternal()— added a deterministic tie-breaker to the constructor sort.Class.getDeclaredConstructors()returns JVM-dependent order that varies across runs (especially under bytecode instrumentation). Tie-breaker prefersStringparameters (most widely convertible viaConverter), then alphabetical type-name. Resolves intermittent ~30% failure rate inClassUtilitiesVarargsArrayStoreTestunder JaCoCo. - BUILD: Added JaCoCo code coverage plugin (
jacoco-maven-plugin0.8.12). Coverage reports are generated on everymvn testattarget/site/jacoco/. No impact on Maven Central deployment. - CORRECTNESS:
ClassValueMap— marked the lazily-populatedcachedEntrySet,cachedValues, andcachedUnmodifiableViewfieldsvolatileto eliminate a dormant JMM bug where threads on the non-synchronized fast path could observe partially-constructed views. No behavior change in practice. - FEATURE:
ClassValueSet.containsClass(Class)— mirrorsClassValueMap.getByClass. Skips the null +o.getClass() != Class.classguard thatcontains(Object)must perform; compiles to a near-directClassValue.get(type)call. Converted 9 hot-path call sites inMultiKeyMapandClassUtilities.SECURITY_CHECK_CACHE. 6 new tests. - FEATURE:
ClassValueMap.getByClass(Class)— typed fast-path lookup that skips theinstanceof Classguard inget(Object), compiling to a near-direct JIT-intrinsifiedClassValue.get(type)load. Converted 30+ hot-path call sites acrossClassUtilities,Converter.ClassPairMap,MultiKeyMap,TypeUtilities,Unsafe,CompactMap,CollectionHandling. 7 new tests. - FEATURE:
ReflectionUtils.getDirectClassAnnotation(Class, Class)— strict JDK-semantics companion togetClassAnnotation(which does a Spring-style full-hierarchy walk regardless of@Inherited). The new method matchesClass.getAnnotation(Class)exactly — right for identity-tied annotations like JPA@Entityor json-io@IoNamingwhere the author deliberately omitted@Inherited. 13 new tests. Purely additive. - PERFORMANCE:
StringUtilities— extended the SIMD-intrinsic bulk-copy pattern (String.getCharsinto a localchar[]) to three more hot paths:levenshteinDistance,damerauLevenshteinDistance, anddecode(String)(hex).damerauLevenshteinDistancedrops from up to 6charAtcalls per inner iter to 0. NewfillCharArray(CharSequence, char[], int)helper falls back tocharAtfor non-StringCharSequence. Restored publicgetChars(String, int)and addedgetChars(String). - PERFORMANCE:
FastReader.readUntil/readLine— eliminated redundantGETFIELDinstructions by caching member fields into locals (per bytecode-level review).readUntilcommon case: 10 → 7 field accesses (-30%). Drain loop wrapped inif (pbPos < pbSize)skips a wastefulPUTFIELDin the empty-pushback common case.readUntilin-loop refill moved to the top of the scan loop, killing an unconditional fill when scan hitsmaxLen. - PERFORMANCE:
FastReader.readLine(char[], int, int)— hoistedb[scanPos]into a single explicit local per scan iteration instead of relying on JIT CSE to coalesce two array loads. Bytecode verification confirmed oneCALOAD(one bounds check) in the hot path. JFR:readLineleaf samples 208 → 170, ~22% self-time reduction, ~1s saved on a 200sJsonPerformanceTestrun. - PERFORMANCE:
Converter— refactored the four internal conversion caches (CONVERSION_DB,FULL_CONVERSION_CACHE,USER_DB,cacheInheritancePairs) fromMap<ConversionPair, V>to a new internalClassPairMap<V>(two-tierClassValueMap<ClassValueMap<V>>). Eliminates per-lookupConversionPairallocation from everyconvert()/isConversionSupportedFor()/getInheritedConverter()call. JFR previously showedConversionPairas a top-5 allocation hotspot during json-io reads; gone from the hot path entirely. - PERFORMANCE:
Converter.USER_DB— removed the redundantinstanceIdfrom its keys (legacy artifact from an earlier static design;USER_DBhas always been a per-Converterinstance field). The parameter is also dropped fromgetInheritedConverter()andaddIdentityConversionIfNeeded(). - REFACTOR:
EncryptionUtilitiesfast-hash methods (fastMD5/fastSHA1/fastSHA256/fastSHA384/fastSHA512/fastSHA3_256/fastSHA3_512) — replaced 7throw new UncheckedIOException(e)calls withExceptionUtilities.uncheckedThrow(e), matching the project's standard pattern (throws the originalIOExceptionwithout wrapping). - SECURITY:
ReflectionUtils.isTrustedCaller()— stack walk no longer misidentifies java-util's own internal cache infrastructure as a trusted caller. Previously any frame starting withcom.cedarsoftware.util.matched, includingLRUCache/LockingLRUCacheStrategy/ThreadedLRUCacheStrategy/ConcurrentHashMapNullSafeframes duringcomputeIfAbsentcallbacks. External callers could bypass dangerous-class blocking through cache-populating lambdas. Fix: newisInfrastructureFrame()helper matches known passthrough classes by exact name. - TESTING: Added 7 JaCoCo coverage-gap test classes —
EncryptionUtilitiesCoverageTest(63 tests),ReflectionUtilsCoverageTest(36),UrlUtilitiesCoverageTest(41),IOUtilitiesCoverageTest(32),DeepEqualsCoverageTest(74),MultiKeyMapCoverageTest(52),ClassUtilitiesAdditionalCoverageTest(48). - TESTING: Added
ReflectionUtilsSecurityTest(13 tests) andClassUtilitiesSecurityTest(28 tests) in thecom.cedarsoftware.testpackage, verifying external-caller security semantics from outside thecom.cedarsoftware.utilpackage.
4.100.0 - 2026-04-10
- 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.
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, avoiding per-character method call and JDK 9+ coder check overhead. Uses.length()for the valid range. Buffer is shared per-thread — valid until the nextgetChars()call on the same thread. - PERFORMANCE:
StringUtilities.hashCodeIgnoreCase(String)— usesStringUtilities.getChars()(SIMD-optimized bulk copy) into a ThreadLocalchar[]buffer, then hashes from the array directly. AvoidscharAt()'s per-character method call and JDK 9+ compact-string coder check overhead. No reflection, no VarHandle, no--add-opens— works on all JDK versions (8-25+). Benchmark shows CaseInsensitiveMap GET improved 70-75% (230 → 58-69 ns/op), PUT improved 48-52% (135 → 65-71 ns/op), and MIXED-CASE GET improved 68% (302 → 93-97 ns/op) 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 (\n,\r,\r\n) into a single call. Uses ac <= '\r'range guard so printable characters (the vast majority) require only one comparison per character instead of two. Eliminates the per-line overhead of separatereadUntil()+read()+ pushback round-trip. JFR shows TOON line-reading samples dropped from 173 to 125 (28% reduction), andFastReader.read()calls halved (53 → 25 samples). - PERFORMANCE:
FastReader.readUntil()pushback drain loop now uses a local variable forpushbackPositioninstead of repeated member field access, avoiding load/store throughthison each iteration. JFR shows 14.8% reduction in aggregate FastReader CPU share. - PERFORMANCE:
FastReader.readUntil()replacedMath.min()call with inline ternary in the tight buffer-scan loop, eliminating method call overhead. JFR confirmed 3.5% wall-clock improvement. - PERFORMANCE:
FastReader.readUntil()now cachesthis.limitinto a local variable afterfill(), eliminating a redundantGETFIELDper outer loop iteration. JFR showsreadUntilself-time dropped ~15%. - PERFORMANCE:
FastReader.fill()extracted core I/O intorefill()which uses a localint nfor thein.read()result instead of writing directly tothis.limiton each retry-loop iteration, reducing PUTFIELD/GETFIELD overhead.fill()simplified to a one-line guard + delegate.
4.98.0 - 2026-03-08
- 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: convertKey 108 + CIS init 95 + CIS hashCode 200).LookupKeyis a lightweight mutable object reused via ThreadLocal, with bidirectional equals support for both HashMap (key.equals(stored)) and ConcurrentHashMap (stored.equals(key)) lookup directions. SortedMap backings (TreeMap, ConcurrentSkipListMap) 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 (map,isMultiKeyMapBacking,isConcurrentBackingMap) 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. Branch ordering optimized so lowercase letters (the most common characters) take only two comparisons. - PERFORMANCE:
StringUtilities.foldCaseForHash()branch ordering optimized to checkc <= 'Z'first, partitioning ASCII so uppercase takes two comparisons and lowercase/symbols take two comparisons, avoiding the previous three-comparison path for lowercase. - BUG FIX:
ConverterDurationToOffsetDateTimeTestused the current system timezone offset to compute expected values, but the conversion target is the epoch (1970). During DST transitions the current offset differs from the epoch offset (e.g., EDT/-04:00 vs EST/-05:00). Fixed to compute the offset at the actual target instant. - MAINTENANCE:
MapUtilities.mapOf()removed stale JDK 1.8 Javadoc comment. - MAINTENANCE:
MultiKeyMapLockStripingTest.testPerformanceWithStriping()removed flaky wall-clock timing assertion. When the single-threaded baseline is ~1ms, thread pool creation overhead makes the ratio meaningless. Correctness assertion retained. - MAINTENANCE:
ExecutorAdditionalTesterror-path tests now suppress theExecutorlogger during intentional error scenarios, eliminating expectedSEVEREstack traces from test output. - TESTING: Added
ToonRoundTripTest— 46 parameterized tests verifying allConverter-supported types round-trip through TOON (write viaJsonIo.toToon(), read back viaJsonIo.fromToon().asClass(originalType)). Includes a POJO-with-converter-fields integration test. - TESTING: Added
ConverterDurationToOffsetDateTimeTest— verifies Duration → OffsetDateTime conversion for zero, positive, negative, complex, custom-timezone, and nanosecond-precision durations.
4.97.0 - 2026-03-03
- 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.
4.96.0 - 2026-02-28
- 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.
4.95.0 - 2026-02-28
- BUG FIX:
ArrayUtilities.setPrimitiveElement(char[], ...)now throwsIllegalArgumentExceptionfor incompatible non-char inputs instead of silently coercing values to'\0'. - BUG FIX:
ArrayUtilitiestype-mismatch errors fromsetElement(...)/setPrimitiveElement(...)now report stableIllegalArgumentExceptionmessages without invoking arbitraryelement.toString()during error construction. - BUG FIX:
ArrayUtilitiesdangerous-class validation now honorsarrayutilities.dangerous.classes.validation.enabledas an independent runtime toggle (default enabled for backward compatibility). - PERFORMANCE:
ArrayUtilitiesnow caches parsed security properties (security,component validation,dangerous validation,max array size, and dangerous-pattern list) with source-based invalidation;toArray()now captures collection size once. - BUG FIX:
ByteUtilitiesnow uses a trusted internal hex-decode path for embedded framework constants (for exampleCompactMaptemplate bytecode), so restrictive user hex-length limits no longer breakCompactMap.builder()initialization. - PERFORMANCE:
ByteUtilitiessecurity configuration now uses a single snapshot cache with property-source invalidation, and multi-byteindexOf/lastIndexOfscans now fast-reject on first/last-byte mismatches before inner comparison. - BUG FIX:
CaseInsensitiveMap.CaseInsensitiveEntry.setValue()now updates both the backing map and the entry view value soentry.getValue()reflects the new value immediately. - BUG FIX:
CaseInsensitiveMap.equals()now rejects false-positive equality when compared maps contain duplicate case-equivalent keys (for example, both"ID"and"id"), preventing collapsed-key matches from being treated as equal. - BUG FIX:
CaseInsensitiveMapkey normalization forMultiKeyMapwithflattenDimensions=falsenow preservesSetsemantics during recursive conversion, so set-based keys remain order-insensitive. - BUG FIX:
CaseInsensitiveMapmutable view operations now correctly mutateMultiKeyMapbackings:keySet()/entrySet()/values()iterator removals andretainAll()now remove backing entries instead of mutating snapshot views. - BUG FIX:
CaseInsensitiveMapwithMultiKeyMapbacking now normalizes keys consistently forConcurrentMapAPIs (compute*,merge,putIfAbsent,remove(k,v),replace*) and when copying from source maps into aMultiKeyMapdestination. - PERFORMANCE:
CaseInsensitiveMapcachesMultiKeyMap.flattenDimensionsat construction time to avoid repeated backing-map casts/lookups in hot multi-key normalization paths. - PERFORMANCE:
CaseInsensitiveMapconcurrent-aware iterators now receive backing concurrency mode directly, removing per-iterator class-name introspection overhead; recursive multi-key normalization now skips unnecessary recursion for scalar elements. - BUG FIX:
CaseInsensitiveSet.equals()now enforces bidirectional set containment checks, removing asymmetric equality outcomes against otherSetimplementations. - BUG FIX:
ClassUtilities.loadResourceAsBytes()now correctly falls back to theClassUtilitiesclass loader when the thread context loader is present but does not contain the requested resource. - BUG FIX:
ClassUtilitiesnamed-parameter varargs construction now treats an omitted varargs parameter as an empty array instead of creating a single null/default element. - PERFORMANCE:
ClassUtilities.findInheritanceMatches()now avoids per-call hierarchy-cache map allocation for single-value candidate sets. - PERFORMANCE:
ClassUtilities.getConstructorPlanCache()now usescomputeIfAbsent()to avoid redundant concurrent cache-map allocations. - PERFORMANCE:
ClassUtilities.loadResourceAsBytes()now reuses resolved class loaders during leading-slash retry, avoiding repeated loader lookups and duplicate probes. - BUG FIX:
ClassValueMap.clear()now atomically replaces theClassValuecache after clearing backing storage, preventing quiescent staleget()reads after concurrentput()/get()/clear()interleavings. - BUG FIX:
ClassValueMap.putIfAbsent()now treats null-mapped entries as absent (for bothClasskeys andnullkey), matchingMapsemantics and fixing downstreammerge()behavior on null-mapped entries. - BUG FIX:
ClassValueMapmutable views now support removal as expected for mutable maps:entrySet().iterator().remove(),entrySet().remove(entry),keySet().remove(key), andvalues().remove(value)all remove backing entries and invalidate per-class cache entries correctly. - PERFORMANCE:
ClassValueMap.clear()now invalidates cached class lookups with a single cache-instance swap instead of key snapshot + per-key invalidation. - PERFORMANCE:
ClassValueMap.computeIfAbsent()now probes backing storage first for non-null hits, avoiding miss-pathClassValuesentinel churn before atomic compute. - PERFORMANCE:
ClassValueMap.unmodifiableView()now returns a cached wrapper instance instead of allocating a new unmodifiable view on each call. - BUG FIX:
ClassValueSet.clear()now atomically invalidates the entireClassValuemembership cache by replacing the cache instance, preventing stalecontains()positives after concurrentclear()interleavings. - PERFORMANCE:
ClassValueSet.retainAll()now removes via a single iterator pass (no intermediate identity set allocation), and iterator-based removals now remove directly from the backing iterator while invalidating only the affected cache entry. - PERFORMANCE:
ClassValueSetnow includes fast paths forretainAll(this),retainAll(empty),removeAll(this), and optimized removal whenremoveAll()is passed anotherClassValueSet;unmodifiableView()now reuses a cached wrapper instance. - PERFORMANCE:
CollectionHandling— collection factory cache (FACTORY_CACHE) now usesClassValueMapinstead ofConcurrentHashMapfor faster O(1) per-class lookups during collection type resolution. - BUG FIX:
CompactMap.CompactMapComparator.compare()— returned 0 for distinct non-Comparablekeys of the same class, causing sorted/reverseCompactMapinstances to overwrite entries. AddedSystem.identityHashCode()tiebreaker so distinct same-class objects are never treated as equal. - BUG FIX:
CompactMap.entrySet().remove()andremoveAll()— removed entries by key only, ignoring the entry's value. Per theSet.remove()contract,entrySet().remove(entry)must only remove when both key and value match. Now delegates tocontains(entry)before removing. - BUG FIX:
CompactMap.equals()compact-array path now rejects duplicate case-insensitive equivalent keys in compared maps, preventing false-positive equality matches. - BUG FIX:
CompactMapconstructor —getNewMap()returning aCompactMap(or subclass likeCompactCIHashMap) is now rejected withIllegalStateException. Using aCompactMapas a backing map creates recursive nested state machines and is never correct — useHashMap,LinkedHashMap,TreeMap, orCaseInsensitiveMapinstead. - BUG FIX:
CompactMapconstructor — legacy subclasses that overrideisCaseInsensitive()to returntruewithout returning aCaseInsensitiveMapfromgetNewMap()now throwIllegalStateExceptionat construction time. Previously, case-insensitive lookups silently stopped working after the map transitioned from compact array state to MAP state. - BUG FIX:
CompactMapnow correctly restores empty-state invariants after map-backed removals (isEmpty()==truewhensize()==0), including map-to-empty transitions through iterator removal. - PERFORMANCE:
CompactMap.equals()compact-array path now uses keyed lookups (and a normalized case-insensitive lookup map when needed) instead of nested scans; non-legacy sorted/reverse iterators skip redundant array re-sort checks. - PERFORMANCE:
CompactMap.putAll()now avoids eager map-state transitions when incoming entries only overwrite existing keys; compact-array maps stay compact when unique key count remains within threshold. - BUG FIX:
CompactSet.withConfig()now preserves the source set's backingmapTypewhen ordering is unchanged, preventing silent downgrades (for example, losingConcurrentHashMapbacking on reconfiguration). - BUG FIX:
CompactSet— case-insensitive equality and hash code now use case-insensitive canonical semantics, restoring contract consistency (equal case-insensitive sets now produce equal hash codes and avoid asymmetric equality against standard case-sensitive sets). - BUG FIX:
ConcurrentHashMapNullSafe.putIfAbsent()now treats null-mapped keys as absent, allowing null-to-non-null replacement for both null and non-null keys. - PERFORMANCE:
ConcurrentHashMapNullSafe.computeIfAbsent()now fast-returns existing non-null mappings before entering the atomic compute path, reducing hit-path overhead. - PERFORMANCE:
ConcurrentHashMapNullSafe.computeIfPresent()now uses a specialized override that avoids default retry/get/replace loops and short-circuits absent/null-mapped keys. - PERFORMANCE:
ConcurrentHashMapNullSafe.entrySet()entries now avoid per-getValue()backing-map lookups while preservingsetValue()write-through behavior. - BUG FIX:
ConcurrentList.addAll(int, Collection)now validates index bounds even when the input collection is empty, restoringListcontract parity with standard JDK implementations. - BUG FIX:
ConcurrentList.forEach()now iterates over a snapshot so callbacks that mutate the list do not deadlock on read-to-write lock upgrades. - PERFORMANCE:
ConcurrentListbulk operations now use single-pass implementations under one write lock:addAll(Collection),addAll(int, Collection),removeAll(), andretainAll(). - BUG FIX:
ConcurrentNavigableMapNullSafe.descendingMap().comparator()now reflects descending-view ordering; descending twice restores the original comparator semantics. - BUG FIX:
ConcurrentNavigableMapNullSafe.wrapComparator()now uses identity-based ordering for same-class non-Comparablekeys, preventing distinct key collisions. - PERFORMANCE:
ConcurrentNavigableMapNullSafenow caches a typed internalConcurrentNavigableMapreference to eliminate repeated hot-path casts in navigation and key-set operations. - BUG FIX:
ConcurrentNavigableSetNullSafe.descendingSet().comparator()now reflects descending-view ordering instead of returning the original comparator unchanged. - PERFORMANCE:
ConcurrentNavigableSetNullSafenow avoids repeated exception-driven null-comparison fallback for custom comparators that reject nulls by caching the null-rejection path after first detection. - PERFORMANCE:
ConcurrentSetnow includes lower-overhead fast paths forremoveAll(this),retainAll(this), and operations against anotherConcurrentSet, plus a fastertoArray(T[])path when no null sentinel is present. - BUG FIX:
Converter.convert()now ignores cachedUNSUPPORTEDsentinel entries, so priorisConversionSupportedFor()calls cannot poison laterconvert()calls into returningnullon unsupported, fallback, or dynamic paths. - BUG FIX:
Converter.isContainerConversionSupported()now reports map-to-map conversions as supported, matchingConverter.convert()runtime behavior forMapsource toMaptarget conversions. - PERFORMANCE:
Converter.addConversion()now invalidates affected conversion cache entries in a single pass across source/target type variations instead of rescanning the full cache once per primitive/wrapper combination. - TESTING:
ConverterFile/Path conversion tests now normalize path separator expectations, andConverterEverythingTestFile/Path assertions now perform separator-normalized comparisons for consistent Mac/Linux/Windows execution. - BUG FIX:
DateUtilitiesregex-timeout controls now honordateutilities.regex.timeout.enabledanddateutilities.regex.timeout.millisecondsduring date parsing. - BUG FIX:
DateUtilitiesstrict parsing now rejects malformed bracketed timezone fragments (for example"[EST"and"EST]") instead of accepting them as valid zones. - PERFORMANCE:
DateUtilitiesnow reduces allocation in malformed-input repetition detection and strict remnant cleanup by using region matching and non-regex marker stripping. - BUG FIX:
DeepEquals.deepEquals(a, b, options)now handlesnulloptions safely for both match and mismatch paths instead of throwingNullPointerExceptionon mismatch output handling. - BUG FIX:
DeepEqualsnow clears stalediff/diff_itemoutput keys at comparison start when reusing an options map, preventing prior mismatch diagnostics from leaking into later successful calls. - PERFORMANCE:
DeepEqualsnow reuses probe-time comparison state in unordered collection/map matching and uses iterator-based reverse traversal for non-RandomAccesslists, reducing allocation churn and linked-list comparison overhead. - BUG FIX:
EncryptionUtilities.decrypt()/decryptBytes()now recover legacy AES/CBC payloads when ciphertext bytes collide with the versioned-GCM header marker, preserving backward compatibility for older encrypted values. - PERFORMANCE:
EncryptionUtilitiesGCM decryption now uses offset-based IV and ciphertext handling instead of per-callArrays.copyOfRange()slices, reducing decrypt-path allocation overhead. - BUG FIX:
Executorarray-command start failures now returnExecutionResult(-1, "", error)/exec(...) == -1, matching documented start-error behavior instead of propagating uncheckedIOException. - BUG FIX:
Executornow forcibly destroys already-started child processes when command execution is interrupted, preventing runaway background command continuation afterexecute(...)returns failure. - BUG FIX:
Executornow updates instance-cachedgetOut()/getError()values on execution failures (including interruption) instead of leaving stale output from prior commands. - PERFORMANCE:
Executornow caches OS shell selection once and uses a shared timeout deadline for gobbler-thread joins, reducing repeated property/string work and worst-case timeout amplification. - TESTING:
ExecutorAdditionalTestinterruption regression now uses a portable Java subprocess probe instead of OS-specific shell timing commands, improving cross-platform reliability. - BUG FIX:
GraphComparator.Delta.Command.fromName()now normalizes command names withLocale.ROOT, avoiding locale-dependent parse failures (for example Turkish-I). - BUG FIX:
GraphComparatornow emits and applies deltas for shadowed fields (same field name across class hierarchy) using unique field lookup keys, preventing dropped or misapplied updates. - BUG FIX:
GraphComparatornow treatsIDcallbacknullreturns as non-ID objects, preventingNullPointerExceptionduring object-id comparison. - PERFORMANCE:
GraphComparatornow caches per-run object-id resolution and avoids redundant map lookups in map comparison paths (get+ conditionalcontainsKeyfallback), reducing hot-path overhead. - BUG FIX:
IdentitySetprobe operations (add,contains,remove) are now bounded by table length, preventing infinite loops when all slots are tombstones (DELETED) and nonullsentinel exists. - PERFORMANCE:
IdentitySetnow rehashes tombstone-heavy tables before insertion and clears tombstones when the set becomes empty, reducing probe-chain growth in delete-heavy workloads. - IMPROVEMENT:
IdentitySetnow supports a configurable load factor viaIdentitySet(int initialCapacity, float loadFactor)while preserving the default0.5behavior for existing constructors. - TESTING:
IdentitySetTestnow includes a boundary-focused load-factor probe matrix (up to 128K ±1), logs study output through JUL, and uses cachedReflectionUtilsfield lookups in probe helpers. - BUG FIX:
IntervalSet.totalDuration()now uses overflow-safe subtraction for defaultNumberandDatemappings, throwingArithmeticExceptioninstead of silently wrapping on extreme ranges. - PERFORMANCE:
IntervalSet.removeIntervalsInKeyRange()now removes and counts in a single iterator pass, andintersection()/intersects()now walk backing map entries directly to avoid per-stepIntervalallocation. - BUG FIX:
IOUtilities.transfer(File, OutputStream)no longer performs a redundant second flush after delegated transfer completion, preventing false post-transfer failures from second-flush exceptions. - BUG FIX:
IOUtilitiesbyte-range APIs now validateoffset/lenbounds forcompressBytes(byte[], offset, len)anduncompressBytes(byte[], offset, len, maxSize), rejecting invalid ranges instead of returning zero-padded synthetic slices. - PERFORMANCE:
IOUtilitiesnow caches parsed timeout, size-limit, and allowed-protocol property values with property-source invalidation, reducing repeated parsing/splitting overhead on hot connection and stream paths. - DEPENDENCY: Updated Jackson test dependencies from 2.20.1 to 2.21.0 (
jackson-databind,jackson-dataformat-xml). - BEHAVIOR:
LockingLRUCacheStrategy.entrySet()/keySet()/values()now return unmodifiable snapshots, eliminating misleading snapshot-mutation behavior where removals appeared to succeed without mutating the cache. - BUG FIX:
LRUCache(LOCKINGandTHREADED) now treats null-mapped keys as absent forputIfAbsent()andcomputeIfAbsent(), allowing conditional writes to populate existing null entries perMapsemantics. - BUG FIX:
LRUCachehashCode()implementations now followMapcontract hashing (sum(entry.hashCode())), restoring consistency withequals()against standard map implementations. - BUG FIX:
MathUtilitiesmin/max varargs overloads now rejectnullarrays withIllegalArgumentExceptioninstead of surfacing rawNullPointerExceptionfrom implicit array dereference. - PERFORMANCE:
MathUtilities.nextPermutation()now uses a RandomAccess fast path and an optimized non-RandomAccess path that permutes an indexed copy and writes back once, improving linked-list traversal performance. - PERFORMANCE:
MathUtilities.parseToMinimalNumericType(CharSequence)now materializes a String at most once and reuses it across parse branches, reducing repeated conversion/allocation work. - PERFORMANCE:
MathUtilitiesnow caches parsed security-limit configuration with property-source invalidation, reducing repeated property parsing overhead on hot min/max/parse/permutation paths. - BUG FIX:
MultiKeyMap.compareNumericValues()—BigDecimal/BigIntegercomparison againstDouble.POSITIVE_INFINITY,NEGATIVE_INFINITY, orNaNthrewNumberFormatExceptionvianew BigDecimal("Infinity"). Non-finite float/double values are now guarded before the BigDecimal conversion path. - BUG FIX:
MultiKeyMap.equals()now rejects compared maps that contain duplicate keys collapsing under MultiKeyMap key equivalence (for example1and1L, or case-variants in case-insensitive mode), preventing false-positive equality and hash-contract violations. - BUG FIX:
MultiKeyMap.hashCode()cache publication is now version-stamped, preventing stale cached hashes from surviving concurrent hash/mutation interleavings. - BUG FIX:
MultiKeyMap.remove(key, value)— returnedtruefor an absent key whenvalue == null. The method now verifies key existence before comparing values, per theMap.remove(Object, Object)contract. - BUG FIX:
MultiKeyMap.replace(key, oldValue, newValue)— incorrectly inserted a new entry when the key was absent andoldValue == null. The method now verifies key existence before comparing values, per theMap.replace(K, V, V)contract. - PERFORMANCE:
MultiKeyMap.putIfAbsent()now normalizes once, reuses precomputed lookup state, and avoids unnecessary resize checks on null-mapped replacement paths. - PERFORMANCE:
MultiKeyMap.remove(key,value)andreplace(...)now use single-pass entry lookup under lock (eliminating redundant second scans when values are null or absent). - PERFORMANCE:
MultiKeyMapcopy constructor — now pre-sizes the internal table from the source's current table size (buckets.length()) instead of its initial capacity, avoiding unnecessary resizes when copying a map that has grown beyond its initial capacity. - PERFORMANCE:
MultiKeyMap— deferredcachedHashCodeinvalidation in 8 conditional mutators (putIfAbsent,computeIfAbsent,computeIfPresent,compute,merge,remove(k,v),replace(k,v),replace(k,old,new)). Cache is now only invalidated inside the lock when mutation actually occurs, avoiding unnecessary invalidation on no-op calls. - PERFORMANCE:
MultiKeyMap— replacedReentrantLockstripe locking withsynchronizedmonitors. Uncontendedsynchronizedcosts ~5-7ns (JDK 18+ thin locks) vs ~15-20ns forReentrantLockAQS machinery, yielding measurable PUT throughput gains. Lock-free reads, compound operation atomicity, and allConcurrentMapcontracts are preserved. Added pre-computedresizeThresholdto eliminate per-PUT multiplication. - PERFORMANCE:
MultiKeyMap— replaced ~50 sequentialclass ==identity checks across 7 hot-path methods withClassValueMapandClassValueSetO(1) lookups:valueHashCode()(12 checks),compareNumericValues()same-type path (10 checks),extractLongFast()(6 checks),isIntegralLike()(6 checks), and leaf-type detection innormalizeKey()/flattenKey()(9 checks). - PERFORMANCE:
MultiKeyMap— zero-allocation GET path and single-allocation PUT path forObject[]keys insimpleKeysMode. GET operations now compute hash inline over the array without creating aMultiKeywrapper. PUT operations use a precomputedMultiKeyconstructor that skips reflection (isArray(),getComponentType(),ArrayUtilities.getLength()). - CLEANUP:
MultiKeyMap— removed 6 hand-unrolledflattenObjectArray1/2/3andflattenCollection1/2/3methods (−162 lines). Benchmarking withsimpleKeysMode=falseconfirmed the JIT-compiledflattenObjectArrayN/flattenCollectionNloops match hand-unrolled performance for 1-3 element keys. All sizes now route through the parameterized N path. - BUG FIX:
ReflectionUtilsMETHOD_CACHEnow isolates exact-signature, arg-count, and non-overloaded lookups to prevent cross-API cache poisoning and incorrect sentinel/method reuse. - BUG FIX:
ReflectionUtilsnow enforces dangerous-class checks consistently acrossgetMethod(Object, String, int),call(Object, String, ...), andgetNonOverloadedMethod(), closing bypass paths that skippedgetMethod(Class, String, Class...)security validation. - PERFORMANCE:
ReflectionUtilsnow caches negative misses for method, field, and annotation lookups using sentinels, reducing repeated hierarchy scans on hot miss paths. - BUG FIX:
RegexUtilities.getRegexTimeoutMilliseconds()now rejects non-positive timeout values and falls back to default timeout to avoid false immediate timeout failures from invalid configuration. - BUG FIX:
RegexUtilitiesnow restores thread interrupt status when timeout-protected regex operations are interrupted, instead of swallowing interrupts in wrappedSecurityExceptionflows. - SECURITY/PERFORMANCE:
RegexUtilitiestimeout execution now uses a bounded daemon thread pool with backpressure instead of an unbounded cached thread pool, preventing timeout-driven worker thread explosions under hostile regex input. - PERFORMANCE:
RegexUtilities.SafeMatchResultnow computes replacement text using start/end ranges instead of invokingMatcher.replaceFirst(""), avoiding an extra regex pass on successfulsafeFind()operations. - BUG FIX:
StringUtilities.hashCodeIgnoreCase()now uses case folding consistent withequalsIgnoreCase()for non-ASCII characters, restoring hash/equality contract behavior for consumers likeCaseInsensitiveMap. - PERFORMANCE:
StringUtilitiesLevenshtein and Damerau-Levenshtein inner loops now use primitiveMath.minpaths instead of varargsMathUtilities.minimum(...), reducing hot-loop overhead. - BUG FIX:
SystemUtilities.getExternalVariable()/getExternalVariableUnsafe()now match documented lookup order (environment first, then system properties) and preserve whitespace-only values instead of treating them as absent. - BUG FIX:
SystemUtilitiessensitive-variable filtering now usesLocale.ROOTcase normalization, preventing locale-dependent bypasses (for example Turkish-I) when security validation is enabled. - PERFORMANCE:
SystemUtilitiestemp-prefix validation now uses a character scan instead of per-call regex matching, and environment-variable collection methods now pre-size result maps to reduce rehash/allocation overhead. - PERFORMANCE:
ThreadedLRUCacheStrategy.putIfAbsent()now avoids unconditional node allocation on hit paths and only allocates when insertion or null-to-non-null replacement is required. - BUG FIX:
TrackingMap.putIfAbsent()fallback (non-concurrent backing maps) now treats null-mapped entries as absent perMapsemantics, allowing null-to-non-null replacement. - PERFORMANCE:
TrackingMap.computeIfPresent()now avoids the unconditional pre-containsKey()probe on hot paths while preserving access-tracking behavior. - PERFORMANCE:
TrackingMap.expungeUnused()now performs size-aware read-key cleanup to reduce work when miss-tracking grows larger than live map keys. - PERFORMANCE:
TrackingMap.keysUsed()now returns a cached unmodifiable view instead of allocating a new wrapper on each call. - BUG FIX:
Traversernow applies stack-depth enforcement only to nodes that survive null/visited/skip filtering, preventing false depth-limit failures on cyclic back-edges. - BUG FIX:
Traversernow deduplicates pending nodes in the traversal frontier and enforces object-visit limits during enqueue, stopping oversized collection expansion earlier undermax.objects.visited. - PERFORMANCE:
Traversernow avoids duplicate reflective field reads for non-container nodes whencollectFields=trueby sharing one field-read pass for bothNodeVisitpayload construction and child scheduling. - BUG FIX:
TTLCache.putIfAbsent()andcomputeIfAbsent()now treat non-expired null-mapped entries as absent, allowing conditional writes to populate null values perMapsemantics. - PERFORMANCE:
TTLCachenow skips LRU list maintenance work whenmaxSize == -1(TTL-only mode), and purge unlinks expired nodes in a single lock section to reduce lock churn. - BEHAVIOR:
TTLCache.entrySet().size()now followsTTLCache.size()best-effort semantics (non-O(n)); iterator traversal still skips expired entries while sizing remains approximate for performance. - BEHAVIOR:
TTLCache.keySet()andvalues()now return unmodifiable snapshots, preventing misleading snapshot mutations that appeared successful without affecting cache contents. - CLEANUP:
TypeHolder— removed class. The super-type-token pattern exists solely for serialization APIs and had zero internal consumers in java-util. The canonicalTypeHolderlives in json-io where it is part of the public API. - BUG FIX:
TypeUtilities.hasUnresolvedType()now inspectsParameterizedTypeowner types, correctly reporting unresolved nested owner generics (for exampleOuter<V>.Inner<String>). - PERFORMANCE:
TypeUtilities.resolveType()now reuses a thread-local cache lookup key and only allocates immutable cache keys on misses, reducing hot-path allocation pressure. - BUG FIX:
UniqueIdGenerator.getServerIdFromVarName()now correctly normalizesInteger.MIN_VALUEinputs (absolute-value modulo 100). - BUG FIX:
UniqueIdGeneratornow detects long-range exhaustion and throwsIllegalStateExceptioninstead of overflowing into negative IDs, preserving monotonic ordering guarantees. - PERFORMANCE:
UniqueIdGenerator.waitForNextMillis()now batches spin checks and reducescurrentTimeMillis()polling frequency in the busy-wait phase. - PERFORMANCE:
Unsafe— serialization constructor cache now usesClassValueMapinstead ofConcurrentHashMapfor faster per-class constructor lookups during constructor-bypassing instantiation.
4.94.0 - 2026-02-14
- PERFORMANCE:
MathUtilities.parseToMinimalNumericType()numeric analysis was refactored to a single-pass scan with no intermediateStringBuilder/substring reconstruction on the hot path. - PERFORMANCE: Added
MathUtilities.parseToMinimalNumericType(CharSequence)overload so callers with buffered number text can avoid extra conversion work before numeric type selection. - PERFORMANCE: Added reusable-buffer constructors for high-throughput JSON pipelines:
FastByteArrayOutputStream(byte[] initialBuffer)FastWriter(Writer out, char[] buffer)FastReader(Reader in, char[] buffer, char[] pushbackBuffer)These allow caller-managed buffer reuse and reduce repeatedbyte[]/char[]allocations in hot String read/write loops.
- PERFORMANCE:
ClassUtilities.newInstance()now caches constructor selection by argument shape (argument count + runtime type/null shape) viaConstructorPlanentries, avoiding repeated constructor re-selection/mismatch work when the same class is instantiated with different argument signatures. - PERFORMANCE:
ClassUtilities.newInstance()varargs matching no longer allocates intermediate parameter/value copies (Arrays.copyOf(...),unused.toArray(), and parameter slice copies). Matching now uses index ranges and pre-sized arrays, reducing allocation pressure in constructor-heavy workloads. - PERFORMANCE:
ClassUtilities.newInstance()map-argument handling now defers positional normalization until named-parameter matching is actually needed, adds a densearg0..argNfast path for positional fallback ordering, and caches per-class named-parameter viability to avoid repeated constructor parameter-name scans. - SECURITY FIX:
ClassUtilities.forName()- Blocked-class checks now apply to array component types for both Java-style names (java.lang.Runtime[]) and JVM descriptors ([Ljava/lang/Runtime;), including when a customClassLoaderis supplied. Added component-type verification after load so blocked classes cannot be exposed through array wrappers. - SECURITY/PERFORMANCE:
ClassUtilities- Bounded negative class-name cache entries per class loader (MAX_NEGATIVE_CLASS_CACHE_ENTRIES) to prevent unbounded memory growth from repeated unique class-miss lookups. - PERFORMANCE:
ClassUtilities.validateAndNormalizeResourcePath()- Replacedsplit("/")traversal-segment check with a single-pass scan and switched case-folding toLocale.ROOT. - BUG FIX:
FastReadernow guards against pathological underlyingReader.read(char[],off,len)implementations that repeatedly return0without progress, throwing a descriptiveIOExceptionafter a bounded retry threshold. - PERFORMANCE:
FastWriter.write(int)removed a redundant pre-write capacity check (single flush check remains), andwrite(char[],off,len)now flushes immediately when the buffer is filled exactly, reducing extra boundary checks in hot paths. - PERFORMANCE:
ClassUtilities- Simplified the constructor argument matching pipeline from 5 phases to 4 by folding primitive/wrapper matching into the inheritance matching phase.ClassHierarchyInfonow includes the primitive/wrapper counterpart at distance 1 (e.g.,Integer's hierarchy includesintat distance 1), sofindInheritanceMatches()handles boxing/unboxing naturally. Removed the separatefindPrimitiveWrapperMatches()phase. - CLEANUP:
ClassUtilities- Deduplicated varargs argument matching code. ExtractedmatchFixedParameters()andpackVarargsArray()helpers frommatchArgumentsWithVarargs(), eliminating ~65 lines of duplicated conversion/packing logic between the "has fixed params" and "no fixed params" branches. - IMPROVEMENT:
Converter- Bridge conversion expansion now iterates until convergence, ensuring all reachable multi-hop conversion paths are discovered regardless of surrogate pair definition order. Previously used a single forward+reverse pass which was sufficient for current pair definitions but fragile to future additions. The convergence loop terminates after 1 iteration for current types (zero cost), but automatically handles deeper chains if new surrogate pairs are added. - IMPROVEMENT:
Converter- Added fullBitSetsimple-type bridge support by registeringBitSet <-> Stringsurrogate/primary pairs, directMap -> BitSetconversion, andBitSet -> BitSetcopy conversion semantics. ExpandedConverterEverythingTestBitSet cross-product coverage so missing BitSet conversion pairs are auto-filled and verified. - API CLEANUP: Removed one-arg
isConversionSupportedFor(Class<?>)from bothcom.cedarsoftware.util.Converterandcom.cedarsoftware.util.convert.Converter. Callers should useisConversionSupportedFor(type, type)for same-type capability checks. - API CLEANUP: Consolidated internal one-arg simple-type call sites to the 2-arg form and removed associated one-arg cache plumbing in
Converterto reduce API surface and maintenance complexity. - COMPATIBILITY: Reintroduced one-arg
isSimpleTypeConversionSupported(Class<?>)as@Deprecatedcompatibility bridges (delegating toisSimpleTypeConversionSupported(type, type)) so projects pinned to olderjson-iobinaries continue to run during migration. - REFACTOR: Simplified
isSimpleTypeConversionSupported(source, target)implementation into a thin semantic gate overisConversionSupportedFor(source, target)while preserving custom-override and container exclusion behavior. - PERFORMANCE:
Converterinheritance-pair cache now usesMap<ConversionPair, InheritancePair[]>keyed directly by(sourceType, targetType)instead ofMultiKeyMap, reducing cache key construction overhead in conversion-support lookups. - MAINTENANCE: Verified
Converter.isConversionSupportedFor(source, target)as the compatibility predicate for json-io scalar fast-path converter gating, enabling downstream simplification away from pair-form "simple type" checks while preserving conversion behavior. - MAINTENANCE: Version bump to 4.94.0, json-io test dependency updated to 4.93.0.
4.93.0 - 2026-02-10
- BUG FIX:
ArrayConversions.enumSetToArray()-ArrayStoreExceptionwhen convertingEnumSettoLong[]. Ordinal values (autoboxedInteger) were stored directly intoLong[]arrays. SplitInteger/Longbranches and added explicit(long)cast. - BUG FIX:
CollectionHandling.sizeOrDefault()-ArrayBlockingQueueoverflow when converting arrays with >16 elements.sizeOrDefault()only handledCollectionsources, returning hardcoded 16 for arrays. AddedArray.getLength()path for array sources. - BUG FIX:
CollectionHandling-SynchronousQueueandDelayQueuenow throw descriptiveIllegalArgumentExceptionwhen used as conversion targets, instead of silently failing at runtime (Queue full/ClassCastException). - BUG FIX:
MapConversions.toThrowable()-ClassCastExceptionwhencauseMessagemap entry was a non-String value (e.g.,Integer). Replaced unchecked(String)cast with.toString(). - BUG FIX:
MapConversions.analyzeTarget()- Failed to detectUnmodifiableSortedMap,UnmodifiableNavigableMap, and their synchronized/checked variants.endsWith("$UnmodifiableMap")missed inner class names like$UnmodifiableSortedMap. Changed tocontains("$Unmodifiable") && endsWith("Map")(and similarly for Synchronized, Checked, Empty, Singleton). - BUG FIX:
MapConversions.toColor()- Packedrgbinteger ignored explicitalphamap entry. When bothrgbandalphakeys were present, alpha bits from the packed int (often 0) overwrote the explicit alpha value. Now decomposes RGB channels and applies explicit alpha separately. - BUG FIX:
MapConversions.copyEntries()- Caught allExceptiontypes, silently swallowing errors during map entry copying. Narrowed toClassCastException | NullPointerException(the only exceptionsMap.put()throws for incompatible entries). - BUG FIX:
NumberConversions.toYear()- UsedshortValue()instead ofintValue(), silently truncating year values outside -32768..32767 (e.g., year 40000 became -25536). - BUG FIX:
NumberConversions.floatingPointToBigInteger()-NaNandInfinitycausedNumberFormatExceptionfromnew BigInteger("NaN"). Added explicit guard with descriptiveIllegalArgumentException. - BUG FIX:
NumberConversions.floatToString()/doubleToString()- Negative zero (-0.0) collapsed to"0"because== 0istruefor both-0.0and+0.0. UsedfloatToRawIntBits/doubleToRawLongBitsto distinguish them, preserving IEEE 754 round-tripping. - BUG FIX:
BigIntegerConversions.toUUID()- Silently truncated values exceeding 128 bits instead of throwing. A 129-bit value would have its high bits dropped, producing a completely wrong UUID. Added overflow check. - BUG FIX:
DateConversions-toString(),toYear(),toYearMonth(),toMonthDay(),toSqlDate()calleddate.toInstant()directly, which throwsUnsupportedOperationExceptiononjava.sql.Date. Replaced with the safetoInstant(from, converter)helper that wraps sql.Date vianew Date(date.getTime()). - BUG FIX:
CalendarConversions-toYear(),toYearMonth(),toMonthDay(),toSqlDate()used the converter's configured zone instead of the Calendar's own timezone, inconsistent withtoZonedDateTime()/toLocalDate()/toLocalDateTime()which correctly use the Calendar's zone. Added directCalendar →registrations for Year, YearMonth, MonthDay, and SqlDate in Converter to bypass the surrogate bridge (which was routing throughZonedDateTimeConversionsand applying the converter's zone). Now all Calendar conversions consistently use the Calendar's own timezone. - PERFORMANCE:
CalendarConversions-toString()created two newDateTimeFormatterinstances per call. Cached as static final fields. - PERFORMANCE:
CalendarConversions-toLong(),toAtomicLong(),toDouble(),toBigDecimal(),toBigInteger()usedcalendar.getTime().getTime()creating an intermediateDateobject. Replaced withcalendar.getTimeInMillis(). - BUG FIX:
InstantConversions.toOffsetDateTime()- UsedSystem.currentTimeMillis()to determine timezone offset instead of the instant's own epoch time. A summer Instant (DST active, e.g. EDT -04:00) converted in winter would incorrectly get the winter offset (EST -05:00). Replaced withinstant.atZone(zoneId).toOffsetDateTime()which determines the correct offset at the instant's point in time. - BUG FIX:
LocalDateConversions.toTimestamp()- Ignored converter's timezone by computing epoch millis viatoEpochDay() * 86400 * 1000(hardcoded UTC midnight). Inconsistent withtoLong(),toDate(),toInstant()which all respect the converter's zone. Replaced withTimestamp.from(toInstant()). - BUG FIX:
LocalDateConversions.toOffsetDateTime()- SameSystem.currentTimeMillis()DST bug as InstantConversions. A summer LocalDate with a DST-observing zone would get the wrong offset. Delegated throughtoZonedDateTime().toOffsetDateTime(). - PERFORMANCE:
LocalDateConversions.toZonedDateTime()- ReplacedLocalTime.parse("00:00:00")withLocalTime.MIDNIGHTconstant. - BUG FIX:
MonthDayConversions.toByte()- Silent overflow for nearly all valid MonthDay values. MMDD format produces 101–1231 but byte range is -128..127, so(byte) 1225silently became(byte) -55. Added overflow check. - BUG FIX:
DurationConversions.toOffsetDateTime()- SameSystem.currentTimeMillis()DST bug. Delegated throughtoZonedDateTime().toOffsetDateTime(). - BUG FIX:
DurationConversions.toSqlDate()- Hardcoded UTC (ZoneOffset.UTC) for day boundary calculation while sibling methodstoLocalDate()andtoLocalDateTime()use the converter's zone. Delegated throughtoLocalDate()for consistency. - BUG FIX:
StringConversions.toLong()- UsedcompareTo() == -1andcompareTo() == 1instead of< 0/> 0for range checks. TheComparable.compareTo()contract only guarantees negative/zero/positive, not specifically -1/0/1. - BUG FIX:
StringConversions.toCharacter()- Silent truncation for digit strings representing values > 65535.(char) Integer.parseInt("65536")silently became'\0'. Added range check to throwIllegalArgumentExceptionfor values outside 0–65535. - BUG FIX:
StringConversions.toColor()-rgb()andrgba()functional notation parsing was case-sensitive."RGB(255,0,0)"or"Rgb(255,0,0)"failed withIllegalArgumentException. Now uses case-insensitive prefix matching. - BUG FIX:
EnumConversions.processElement()- String elements in collections/arrays bypassed themaxEnumNameLengthguard when converting toEnumSet. ThestringToEnum()method enforced the limit, butprocessElement()calledEnum.valueOf()directly without the length check. Now applies the same guard consistently. - BUG FIX:
PatternConversions.toMap()/MapConversions.toPattern()- Pattern flags (CASE_INSENSITIVE,MULTILINE, etc.) were lost during serialization.toMap()only stored the pattern string, andtoPattern()compiled without flags. NowtoMap()includes a"flags"entry when non-zero, andtoPattern()reads it to restore the original flags. - BUG FIX:
PathConversions.toFile()-UnsupportedOperationExceptionpropagated uncaught when converting aPathfrom a non-default filesystem (e.g., zip filesystem) toFile.Path.toFile()only works for the default filesystem provider. Now catchesUnsupportedOperationExceptionand wraps it in a descriptiveIllegalArgumentException. - BUG FIX:
DimensionConversions.toInteger()- Silent integer overflow when computing area (width * height). For example, exceedsInteger.MAX_VALUEand wrapped to a negative value. Replaced withMath.multiplyExact()which throwsArithmeticExceptionon overflow. - BUG FIX:
PointConversions.toInteger()/toLong()/toBigInteger()- Silently discarded the Y coordinate, returning only X. A 2D Point has no meaningful single-number representation. Now throwsIllegalArgumentException, consistent withtoBigDecimal()which already threw. - BUG FIX:
RectangleConversions.toInteger()- Silent integer overflow when computing area (width * height). Replaced withMath.multiplyExact()which throwsArithmeticExceptionon overflow. - BUG FIX:
RectangleConversions.toInsets()- Silent integer overflow when computingbottom(y + height) andright(x + width). Replaced withMath.addExact()which throwsArithmeticExceptionon overflow. - BUG FIX:
InsetsConversions.toInteger()- Silent integer overflow when summingtop + left + bottom + right. Replaced with chainedMath.addExact()calls. - BUG FIX:
InsetsConversions.toDimension()- Silent integer overflow when computingleft + right(width) andtop + bottom(height). Replaced withMath.addExact(). - BUG FIX:
InsetsConversions.toRectangle()- Silent integer overflow when computingright - left(width) andbottom - top(height). Replaced withMath.subtractExact(). - PERFORMANCE:
ConverterOptions.getCustomOptions()/getConverterOverrides()- Default implementations allocated a newHashMap<>()on every call. Replaced withCollections.emptyMap()since the defaults represent empty, read-only maps. - CLEANUP: Removed ~131 dead conversion methods across 25 source files. These methods were not registered in
Converter.CONVERSION_DBand not called internally by any registered method. The surrogate/bridge system (e.g.,X → Long → AtomicLong,Calendar → ZonedDateTime → *) auto-chains these conversions, making the explicit methods redundant. Key removals: alltoAtomicLongfrom 9 temporal classes, geometry cross-conversion methods fromDimensionConversions/PointConversions/RectangleConversions/InsetsConversions, 26 geometry+atomic methods fromNumberConversions(807→366 lines), all numeric methods fromMonthDayConversions, 25 unused array/atomic/string methods fromUniversalConversions(1035→726 lines), and atomic bridge methods fromBooleanConversions/CharacterConversions/StringConversions/CalendarConversions/BigDecimalConversions/UrlConversions/ColorConversions/DurationConversions/OffsetTimeConversions/YearConversions. All 18,652 existing tests pass. - CLEANUP: Temporal hub-and-spoke — extended the surrogate/bridge system to treat
Instant,LocalDateTime,LocalDate,OffsetDateTime,Date, andTimestampas surrogates ofZonedDateTime. Removed ~45 passthrough methods that simply chainedX → ZonedDateTime → Target(e.g.,InstantConversions.toLocalDate()wastoZonedDateTime(from, converter).toLocalDate()). The bridge system now auto-generates these composite conversions at init time. Removed methods fromInstantConversions(5),LocalDateTimeConversions(15),LocalDateConversions(10),OffsetDateTimeConversions(7), andDateConversions(8). Net gain of 4 new conversion paths discovered by the bridge (e.g.,Instant → MonthDay/Year/YearMonth,LocalDate → LocalTime). All 18,640 tests pass. - BUG FIX:
ObjectConversions.convertFieldValueIterative()- Complex nested objects inside collections, maps, and arrays were converted totoString()(e.g.,"Foo@1a2b3c4"), losing all structured data. Now recursively converts them to Map representations preserving field names and values. - PERFORMANCE:
ObjectConversions.isRecord()-ReflectionUtils.getMethod(Class.class, "isRecord")was called on every invocation. Cached theMethodreference in a static final field. - PERFORMANCE:
ObjectConversions.convertToJsonCompatible()- Numbers were round-tripped throughtoString()→parseToMinimalNumericType(), which could change types (e.g.,int 30→Long 30). Standard numeric types now pass through directly. - BUG FIX:
UniversalConversions- 7 bidirectional array methods (byteArrayToByteArray,integerArrayToIntArray, etc.) always threwClassCastExceptionin the else branch. Cast(primitive[]) (Object) Wrapper[]is invalid —Byte[]cannot be cast tobyte[]. Changed return types toObjectto support both directions. - BUG FIX:
UniversalConversions- 5 NIO buffer-to-array methods (intBufferToIntArray,longBufferToLongArray, etc.) destroyed pre-existing marks on the source buffer viamark()/reset(). Replaced withduplicate().get()which reads from a copy, leaving the original buffer's position and mark untouched. - PERFORMANCE:
UniversalConversions-intArrayToAtomicIntegerArray()andlongArrayToAtomicLongArray()used manual element-by-element loops instead ofnew AtomicIntegerArray(int[])/new AtomicLongArray(long[])constructors which copy the array directly. - BUG FIX:
CharacterArrayConversions- Null elements inCharacter[]produced literal"null"string instead of being skipped.StringBuilder.append(Object)converts null to"null", so{'a', null, 'b'}became"anullb". Now skips null elements intoString(),toStringBuilder(), andtoStringBuffer(). - BUG FIX:
TimeZoneConversions.toZoneOffset()- UsedgetRawOffset()which ignores DST. For a zone currently in daylight saving (e.g., Australia/Sydney in February = AEDT +11:00), returned the standard offset (+10:00) instead. Now delegates throughtoZoneId().getRules().getOffset(Instant.now()), consistent withZoneIdConversions.toZoneOffset(). - JAVA 25+ PREP:
Unsafe- AddedReflectionFactory.newConstructorForSerialization()as preferred strategy for constructor-bypassing instantiation, falling back tosun.misc.Unsafeonly when ReflectionFactory is unavailable. Caches serialization constructors per class. - BUG FIX:
RegexUtilities- Pattern caches usedConcurrentHashMapwhich rejects null fromcomputeIfAbsent, causingNullPointerExceptionon every invalid regex pattern. Switched toConcurrentHashMapNullSafe. - BUG FIX:
GraphComparator.processPrimitiveArray()-NullPointerExceptionwhen both source and target array elements were null (missing null guard before.equals()call). - BUG FIX:
CompactLinkedSet.isCaseInsensitive()- Returnedtrueinstead offalse(copy-paste error from case-insensitive variant). - BUG FIX:
IOUtilities- Native memory leak when handling deflate-encoded streams. CustomInflaterpassed toInflaterInputStreamwas neverend()ed. Switched to default constructor soclose()manages the Inflater lifecycle. - BUG FIX:
MultiKeyMap- ThreadLocal array reuse ingetMultiKey()/containsMultiKey()was unsafe under reentrant calls (e.g., customequals()/hashCode()calling back into the map). Added reentrance detection that falls back to fresh allocation. - BUG FIX:
IdentitySet.resize()- Integer overflow when atMAX_CAPACITY(1<<30).oldCapacity << 1wrapped toInteger.MIN_VALUE, causingNegativeArraySizeException. Added capacity guard. - BUG FIX:
SystemUtilities.getNetworkInterfaces()-NullPointerExceptionwhenNetworkInterface.getNetworkInterfaces()returns null (valid per JDK docs when no interfaces exist). - BUG FIX:
AbstractConcurrentNullSafeMap.computeIfAbsent()- Mapping function called twice when key was mapped to null, violating the Map contract (at most once). Replaced with singlecompute()call. - BUG FIX:
CompactMap.equals()- Null keys skipped during equality comparison in compact array state. Removed erroneousentries[i] != nullguard;areKeysEqual()already handles nulls viaObjects.equals(). - BUG FIX:
TTLCache.get()- Race condition where background purge thread could nullnode.valueviaunlink()between the expiry check and value read. Fixed by capturing value before expiry check. - BUG FIX:
ReflectionUtils- TOCTOU race ingetDangerousClassPatterns()/getSensitiveFieldPatterns()where two separate volatile fields could be read in a mismatched state. Combined into singleSimpleImmutableEntryvolatile reference. - BUG FIX:
UrlUtilities.validateContentLength()- Used rawmaxContentLengthfield instead ofgetConfiguredMaxContentLength(), ignoring system property override. - BUG FIX:
FastByteArrayOutputStream.write()- Integer overflow whencount + lenexceededInteger.MAX_VALUEsilently skipped buffer growth, leading toArrayIndexOutOfBoundsException. Added overflow detection. - BUG FIX:
TestUtil.assertContainsIgnoreCase()- Used Javaassertkeyword which is a no-op without-eaJVM flag. Replaced with explicitthrow new AssertionError(...). - DOC FIX:
ArrayUtilities.createArray()- Javadoc incorrectly declared@throws NullPointerException; method actually returns null for null input. - TEST FIX:
LRUCacheOverflowTest- Stabilized flaky microbenchmark: runs 5 rounds and compares best times, raised threshold from 3x to 5x. - TEST FIX:
ConverterArrayCollectionTest.testCachingPerformance- Stabilized flaky performance test: added JIT warmup, best-of-3 rounds, raised threshold from 500ms to 750ms. - PERFORMANCE:
FastReader.readUntil()- Optimized hot inner loop (~37% faster): localizedpositionfield to stack variable to enable register allocation, precomputed loop bound to a single condition, and deferredtotalReadcounter update to loop exit. - PERFORMANCE:
FastWriter.write(String)- Optimized fast path: localizednextCharfield to stack variable, removed redundantlen == 0early return (handled naturally by fast path), cachedstr.length()to local, simplified bounds check. - FEATURE:
Converter- AddedUUID ↔ byte[]direct conversions. UUID is converted to/from a 16-byte big-endian array (most significant bits first). Validates that byte[] is exactly 16 bytes. - FEATURE:
Converter- AddedUUID ↔ ByteBufferdirect conversions. These cannot be auto-generated by the bridge system because arrays are excluded from surrogate bridging. ByteBuffer usesasReadOnlyBuffer()to avoid mutating the original buffer's position. - FEATURE:
Converter- AddedMonthDay ↔ numericandYearMonth ↔ numericconversions (Short, Integer, Long, Float, Double, BigInteger, BigDecimal) in both directions. MonthDay uses MMDD encoding (e.g., June 15 = 615). YearMonth uses YYYYMM encoding (e.g., June 2024 = 202406). Also added missingBigDecimal → MonthDayandBigDecimal → YearMonthregistrations. - FEATURE:
Converter- AddedLocalTime ↔ OffsetTimedirect conversions. - FEATURE:
Converter- Added self-conversions forStringBufferandStringBuilder(clone semantics — returns new instance with same content). - BUG FIX:
Converter- Mutable type self-conversions forbyte[]andTimeZonereturned the same instance viaConverter::identity. Now returns a defensive copy (Arrays.copyOffor byte[],TimeZone.clone()for TimeZone). Consistent withAtomicBoolean/AtomicInteger/AtomicLongwhich already cloned. - CLEANUP: Removed 14 unused conversion methods:
toStringBuffer()andtoStringBuilder()fromByteArrayConversions,ByteBufferConversions,CharacterArrayConversions,CharArrayConversions,CharBufferConversions;toSqlDate(),toYear(),toYearMonth(),toMonthDay()fromLocalDateConversions;toOffsetDateTime()fromLocalDateTimeConversions;toString()fromStringBufferConversionsandStringBuilderConversions. - TEST FIX:
ConverterEverythingTest- RemovedTimeZonefrom immutable type set (TimeZone is mutable — hassetRawOffset()etc.). Self-conversion now correctly returns a clone.
4.92.0 - 2026-02-08
- PERFORMANCE:
CaseInsensitiveMap- Significant speedup by removing the global LRU cache and constructingCaseInsensitiveStringwrappers directly on the heap. The cache'sConcurrentHashMaplookup + LRU bookkeeping cost more per call than simply creating the lightweight wrapper. CaseInsensitiveMap(HashMap) is now ~4x faster than TreeMap(CASE_INSENSITIVE_ORDER), up from ~9x slower. - BUG FIX:
CaseInsensitiveString.equals()- Fixed incorrect equality when hash code was 0 (skippedequalsIgnoreCase()check). - CLEANUP: Deprecated
replaceCache(),resetCacheToDefault(), andsetMaxCacheLengthString()as no-ops. System propertiescaseinsensitive.cache.sizeandcaseinsensitive.max.string.lengthno longer have any effect.
4.91.0 - 2026-02-08
- BUG FIX:
DeepEquals- Enum constants with class bodies misclassified asTYPE_MISMATCHClass.isEnum()returnsfalsefor anonymous subclasses created by enum constants with bodies (e.g.,FOO { @Override ... })- 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_MISMATCHinstead ofVALUE_MISMATCH - Fixed by using
instanceof Enuminstead ofClass.isEnum(), which correctly handles anonymous enum subclasses
- BUG FIX:
DeepEquals- Asymmetric simple-type check produced order-dependent error messages- Only
key1's type was checked for the simple-type fast path; whenkey1was a simple type (e.g.,String) butkey2was a complex type (e.g.,List), the comparison entered the simple-type branch based onkey1alone deepEquals("hello", list)producedVALUE_MISMATCHwhiledeepEquals(list, "hello")producedCOLLECTION_TYPE_MISMATCHfor the same comparison- Fixed by requiring both sides to be simple types before entering the fast path, falling through to container/class-equality checks for correct symmetric
TYPE_MISMATCHreporting
- Only
- PERFORMANCE:
DeepEquals.decomposeOrderedCollection()- Eliminated unnecessaryArrayListcopy for Deque comparisons- When comparing two Deques (not Lists), both collections were copied to
ArrayList(O(n) allocation + copy) just for index-based access - Fixed by using iterator-based forward traversal with an array buffer for Deques, while preserving direct indexed access for Lists
- When comparing two Deques (not Lists), both collections were copied to
- CLEANUP:
DeepEquals- Removed unusedSCALE_DOUBLEandSCALE_FLOATconstants (dead code from prior refactoring) - CLEANUP:
DeepEquals- Fixed misleading comments innearlyEqual()methods that incorrectly stated bitwise equality handles+0.0 == -0.0(it does not; the tolerance check handles it) - BUG FIX:
MultiKeyMap- Concurrent resize lost entries due to stripe lock / bucket mismatchgetStripeIndex()computed the stripe from(spread(hash) & bucketMask) & STRIPE_MASK, wherebucketMaskdepends on the current table size. Between computing the stripe and acquiring the lock, a concurrent resize could replacebucketswith a larger table. The*NoLockmethods then re-read the new table and computed a bucket index that mapped to a different stripe, allowing two threads to modify the same bucket concurrently — a lost-update race that silently dropped entries- Fixed by making
getStripeIndex()table-size-independent (spread(hash) & STRIPE_MASK), enforcing minimum capacity >=STRIPE_COUNTso same-bucket always means same-stripe, and using the locally captured table reference consistently inputNoLock()/removeNoLock()
- BUG FIX:
UrlUtilities.setCookies()- Error message incorrectly said "AFTER" instead of "BEFORE" callingconnect()- The
IllegalStateExceptionhandler told users to callsetCookies()after connecting, but cookies must be set beforeconnect()— the opposite of what the message said - Fixed by changing "AFTER" to "BEFORE" in the error message
- The
- BUG FIX:
UrlUtilities-NumberFormatExceptionon invalid system properties for max download size / max content length- Four methods (
getConfiguredMaxDownloadSize(),getConfiguredMaxContentLength(),getMaxDownloadSize(),getMaxContentLength()) calledLong.parseLong()/Integer.parseInt()without catchingNumberFormatException - A non-numeric system property value (e.g.,
urlutilities.max.download.size=abc) crashed with an unhandled exception instead of falling back to the default - Fixed by catching
NumberFormatExceptionand falling through to the default value
- Four methods (
- BUG FIX:
TTLCache.put()- Always returnednullinstead of the previous valueunlink(oldEntry.node)setsnode.value = nullbefore the return value was read, soput()always returnednulleven when replacing an existing entry- Fixed by saving the old value before calling
unlink()
- BUG FIX:
TTLCache.equals()- Returnedfalseincorrectly when expired-but-not-yet-purged entries existedequals()delegated toAbstractSet.equals()which short-circuits onentrySet().size()mismatch;size()included expired entries but the iterator skipped them- Fixed by rewriting
equals()as a single-pass comparison over non-expired entries, avoiding the size/iterator inconsistency entirely
- BUG FIX:
TrackingMap.remove(key, value)/replace(key, oldValue, newValue)- Non-concurrent fallbacks returnedtruefor absent keys when value wasnull- Both fallback paths used
Objects.equals(curValue, value)without checkingcontainsKey(), soObjects.equals(null, null)matched absent keys remove("absent", null)incorrectly returnedtrue;replace("absent", null, "x")returnedtrueand inserted a spurious entry- Fixed by adding the
containsKeyguard matching the JDK's defaultMap.remove(key, value)andMap.replace(key, old, new)contracts
- Both fallback paths used
- BUG FIX:
StringUtilities.regionEqualsIgnoreCase()- ASCII case folding matched non-letter characters- The condition
(c1 - 'A') <= 25used signed arithmetic, so it was true for all chars from NUL (0) through 'Z' (90), not just 'A'-'Z' - This caused false positives for 6 non-letter character pairs (e.g., ';' matched '[', '@' matched '`') when comparing non-String CharSequences case-insensitively
- Fixed by using explicit range check:
c1 >= 'A' && c1 <= 'Z'
- The condition
- BUG FIX:
StringUtilities.wildcardToRegexString()-+not escaped in generated regex- The regex quantifier
+was not in the escape list, so a wildcard like"a+b"produced regex^a+b$matching"ab","aab", etc. instead of only the literal"a+b" - Fixed by adding
+to the set of escaped regex metacharacters
- The regex quantifier
- BUG FIX:
CollectionUtilities.listOf()/setOf()- Null elements silently accepted despite documented NPE- Both methods documented
@throws NullPointerExceptionfor null elements (matchingList.of()/Set.of()contract), butArrayList.add(null)andLinkedHashSet.add(null)do not throw - Fixed by adding explicit
Objects.requireNonNull()checks in both methods
- Both methods documented
- BUG FIX:
ClassUtilities-CLASS_NOT_FOUND_SENTINELusingVoid.classmasked legitimatejava.lang.Voidlookups- The "class not found" cache sentinel was
Void.class, so the firstforName("java.lang.Void", cl)call succeeded, but subsequent calls foundVoid.classin the cache, matched the sentinel, and incorrectly threwClassNotFoundException - Fixed by replacing the sentinel with a private inner class (
ClassNotFoundSentinel) that can never collide with any real class
- The "class not found" cache sentinel was
- BUG FIX:
RegexUtilities.getRegexTimeoutMilliseconds()-NumberFormatExceptionon invalid system property- Unlike the boolean config methods which gracefully handle invalid input, this method threw an unhandled
NumberFormatExceptionifcedarsoftware.regex.timeout.millisecondswas set to a non-numeric value - Fixed by catching
NumberFormatExceptionand falling back to the default timeout (5000ms)
- Unlike the boolean config methods which gracefully handle invalid input, this method threw an unhandled
- BUG FIX:
ReflectionUtils.isTrustedCaller()- Dangerous class security check was always bypassedisTrustedCaller()foundReflectionUtilsitself on the call stack, which always matched thecom.cedarsoftware.util.trusted prefix- This made
isDangerousClass()always returnfalse, completely disabling the dangerous class security feature even when enabled - Fixed by skipping
ReflectionUtilsframes when walking the stack, so only actual external callers are evaluated
- BUG FIX:
MathUtilities-minimum()/maximum()forBigIntegerandBigDecimaldid not null-checkvalues[0]when array had 2+ elements- Calling e.g.
minimum(null, BigInteger.ONE)produced a confusingNullPointerExceptionfromBigInteger.compareTo()instead of the friendlyIllegalArgumentException - Fixed by moving the null check before the loop, which also eliminates the redundant
len == 1special case
- Calling e.g.
- PERFORMANCE:
ThreadedLRUCacheStrategy.computeIfAbsent()- Eliminated unnecessary eviction checks on cache hitscomputeIfAbsent()incrementedinsertsSinceEvictionon every call, including cache hits- This triggered unnecessary eviction scans even when no new entry was added
- Fixed by tracking whether the mapping function was invoked, only running eviction logic on actual inserts
- CLEANUP:
ThreadedLRUCacheStrategy- Removed unusedsoftCapfield andSOFT_CAP_RATIOconstant- The "Zone C probabilistic inline eviction" described in the Javadoc was never implemented
- Updated Javadoc to accurately describe the actual eviction zones
- BUG FIX:
LockingLRUCacheStrategy.clear()- Concurrentget()could corrupt the linked list afterclear()clear()reset the head/tail sentinels but did not null out removed nodes'prev/nextlinks- A concurrent
get()usingtryLockcould callmoveToHead()on a node with stale links, splicing ghost nodes into the live list - This caused
entrySet(),containsValue(),hashCode(), andequals()to return stale data - Fixed by walking the list and nulling each node's links before resetting the sentinels
- BUG FIX:
LoggingConfig.init()- Missinginitializedguard allowed repeated reconfiguration- The no-arg
init()did not check theinitializedflag, unlikeinit(String)which did - In test environments, every class with
static { LoggingConfig.init(); }re-captured the full stack trace and overwrote formatters - User-configured formats set via
init(String)could be silently overwritten by subsequentinit()calls
- The no-arg
- PERFORMANCE:
IOUtilities.compressBytes(FastByteArrayOutputStream, FastByteArrayOutputStream)- Eliminated unnecessary buffer copy- Used
writeTo(gzipStream)(zero-copy) instead oftoByteArray()(allocates full copy), matching theByteArrayOutputStreamoverload
- Used
- PERFORMANCE:
IOUtilities.compressBytes(byte[], int, int)- Removed redundant double copytoByteArray()already returns a correctly-sized copy; the outerArrays.copyOf()was redundant
- BUG FIX:
IdentitySet.addInternal()- Element duplication after removal via tombstone slots- When a DELETED tombstone appeared before an existing element in the probe chain,
addInternal()inserted a duplicate without checking further, causingadd()to returntruefor elements already present, inflatedsize, and ghost entries that survivedremove() - Fixed by remembering the first DELETED slot but continuing to probe until
nullor finding the element
- When a DELETED tombstone appeared before an existing element in the probe chain,
- BUG FIX:
IdentitySetconstructor - Infinite loop forinitialCapacity > $2^{3}$0- The power-of-2 rounding loop overflowed
int, causingcapacityto go negative then zero, looping forever - Fixed by clamping the target to
[1, $2^{30}$]before the loop
- The power-of-2 rounding loop overflowed
- BUG FIX:
Executor-InterruptedExceptionswallowed without restoring thread interrupt flag- Both
execute()methods now callThread.currentThread().interrupt()before returning - Bounded gobbler thread
join()calls with timeout to prevent indefinite hangs afterdestroyForcibly()
- Both
- BUG FIX:
ExceptionUtilities.getDeepestException()- Infinite loop on circular exception cause chains- Added cycle detection using
IdentitySetto safely handle circular chains (e.g. A→B→A) - Fixed Javadoc on
safelyIgnoreException(Throwable)that incorrectly claimedThreadDeathwas rethrown
- Added cycle detection using
- BUG FIX:
EncryptionUtilities- All 7fast*file-hashing methods (fastMD5,fastSHA1,fastSHA256,fastSHA384,fastSHA512,fastSHA3_256,fastSHA3_512) never used the optimizedFileChannelpathFiles.newInputStream()returnsChannelInputStreamon Java 9+, notFileInputStream, so theinstanceof FileInputStreamcheck always failed- Every call fell through to the slower
InputStream.read(byte[])path instead of the intendedFileChannel/ByteBufferpath - Fixed by using
new FileInputStream(file)directly, which guarantees theFileChanneloptimization is used
- PERFORMANCE:
ByteUtilities- Cached security property lookups for better performance- Security-related
System.getProperty()calls are now cached with property change detection - Eliminates repeated property parsing on every
encode()/decode()call - Cache automatically refreshes when property values change
- Security-related
- BUG FIX:
ByteUtilities.encode()- Added integer overflow protection- Arrays larger than
Integer.MAX_VALUE / 2now throwIllegalArgumentException - Previously would cause
NegativeArraySizeExceptiondue tobytes.length * 2overflow
- Arrays larger than
- PERFORMANCE:
ByteUtilities.indexOf()- Added fast path for single-byte patterns- Single-byte pattern searches now use optimized loop without nested iteration
- FEATURE:
ByteUtilities- Added new search methodslastIndexOf(byte[] data, byte[] pattern, int start)- Find last occurrence searching backwardslastIndexOf(byte[] data, byte[] pattern)- Find last occurrence from endcontains(byte[] data, byte[] pattern)- Check if pattern exists in data
- CLEANUP:
ByteUtilities.HEX_ARRAYis now private- Use
ByteUtilities.toHexChar(int)public API instead StringUtilitiesupdated to usetoHexChar()method
- Use
- MAINTENANCE: Fixed flaky
IOUtilitiesProtocolValidationTest.testProtocolValidationPerformancetest- Added warmup iterations to allow JIT compilation before timing
- Increased threshold from 100ms to 500ms for CI environments with variable performance
- MAINTENANCE: Fixed flaky
UniqueIdGeneratorTesttiming tests- Increased threshold from 2ms to 50ms for CI environments with thread scheduling delays
- MAINTENANCE: Fixed flaky
MultiKeyMapLockStripingTest.testPerformanceWithStripingtest- Increased slowdown tolerance from 5x to 10x for CI environments with shared resources
- BUG FIX:
MultiKeyMap-cachedHashCodenot invalidated by ConcurrentMap methodsputIfAbsent,computeIfAbsent,computeIfPresent,compute,merge,remove(K,V),replace(K,V),replace(K,V,V)all bypassedcachedHashCode = nullinvalidation- After any of these methods mutated the map,
hashCode()returned a stale cached value
- BUG FIX:
MultiKeyMap-compareCollectionsskips trailing elements after Set sections- Unconditional
i++at end of while loop overcounted after Set branch which already advancesi - Elements following a Set in an expanded key were never compared, causing false key matches
- Unconditional
- BUG FIX:
MultiKeyMap- Small Set comparison (<=6 elements) doesn't track consumed matches- Under
valueBasedEquality, two distinct elements in set1 could match the same element in set2 - For example,
Integer(1)andLong(1L)both matchingInteger(1), producing false equality - Fixed with
boolean[]consumed tracking in all three comparison methods
- Under
- BUG FIX:
MultiKeyMap- Stripe contention diagnostics track wrong stripeputInternalandremoveInternalusedhash & STRIPE_MASKbutgetStripeLockuses(hash & tableMask) & STRIPE_MASK- When table size < stripe count, per-stripe metrics were attributed to incorrect stripes
- Extracted shared
getStripeIndex()helper for consistent stripe computation
- BUG FIX:
MultiKeyMap- ThreadLocal lookup arrays leak referencesgetMultiKey()andcontainsMultiKey()methods never nulled out array entries after use- In thread-pool environments, this pinned references to user objects for the lifetime of the thread
- Added
try/finallycleanup in all 8 methods
- PERFORMANCE:
MultiKeyMap- Hash spreading for better bucket distribution- Added
spread(h) = h ^ (h >>> 16)at all bucket selection points (same technique asConcurrentHashMap) - When the table is small, only low-order bits select the bucket; spreading mixes in higher bits to reduce collisions
- Applied at all 7 bucket index computation sites; stored hashes are unchanged
- Added
- PERFORMANCE:
MultiKeyMap-keySet()andvalues()no longer rebuild fullentrySet()values()now iterates buckets directly, skipping all key reconstruction (reconstructKey()) andSimpleEntryallocationkeySet()now iterates buckets directly, skippingSimpleEntrywrapper allocation
- PERFORMANCE:
MultiKeyMap- Lock contention tracking is now opt-in viatrackContentionMetrics(true)- Eliminates 2+
AtomicIntegerCAS operations perput/removecall when tracking is disabled (default) - Also skips the
tryLock()-then-lock()contention detection pattern, using a singlelock()call instead - Enable via
MultiKeyMap.builder().trackContentionMetrics(true)when diagnostics are needed
- Eliminates 2+
- PERFORMANCE:
CaseInsensitiveMap- Eliminate double lookup inequals()- Replaced
containsKey()+get()(twoconvertKey()calls and two hash probes per entry) with singleget(), falling back tocontainsKey()only for null values
- Replaced
- PERFORMANCE:
CaseInsensitiveMap- CachekeySet()andentrySet()view objects- Previously created new
AbstractSetinstances on every call; now cached intransientfields matching the JDKAbstractMap/HashMappattern
- Previously created new
- BUG FIX:
AbstractConcurrentNullSafeMap-merge()incorrectly invokes remapping function when existing value is null- When a key was mapped to
null(stored internally asNullSentinel.NULL_VALUE), the sentinel made null appear non-null to the backing map'smerge() - The remapping function was incorrectly called with
(null, newValue)instead of just inserting the new value per themerge()contract
- When a key was mapped to
- PERFORMANCE:
AbstractConcurrentNullSafeMap-equals()andhashCode()iterate internal map directly- Previously iterated
entrySet()wrappers where eachEntry.getValue()did a full hash probe on the backing map - Also eliminated
containsKey()+get()double lookup on the other map inequals()
- Previously iterated
- PERFORMANCE:
AbstractConcurrentNullSafeMap- CachekeySet(),entrySet(), andvalues()view objects- Previously created new anonymous instances on every call; now cached in
transientfields
- Previously created new anonymous instances on every call; now cached in
- BUG FIX:
CaseInsensitiveMap-entrySet().remove()andremoveAll()ignore entry value- Both methods only checked the key, removing entries even when the value didn't match
- Violates the
Set<Map.Entry>contract which requires both key AND value to match - Fixed:
remove()now checksObjects.equals()on the value before removing;removeAll()delegates to the fixedremove()
- BUG FIX:
MultiKeyMap-hashCode()uses case-sensitive hashing for case-insensitive mapshashCode()iteratedentrySet()which reconstructs original-case String keys, then usedString.hashCode()(case-sensitive)- Two equal case-insensitive MultiKeyMaps with different-case keys produced different hashCodes, violating the hashCode contract
- Fixed: case-insensitive mode now iterates internal buckets using pre-computed case-insensitive key hashes
- BUG FIX:
ClassValueSet-clear()race condition allows permanently stale cache entries- Cache was invalidated BEFORE clearing the backing set, allowing a concurrent
contains()to re-cache staletruevalues that would never be invalidated - Fixed: snapshot keys, clear backing set first, then invalidate cache (matching
ClassValueMappattern)
- Cache was invalidated BEFORE clearing the backing set, allowing a concurrent
- PERFORMANCE:
ClassValueSet- Remove unnecessary O(n) copy initerator()- Previously copied entire backing set into an
IdentitySetsnapshot on everyiterator()call ConcurrentHashMap.newKeySet()already provides weakly-consistent iterators that never throwConcurrentModificationException
- Previously copied entire backing set into an
- PERFORMANCE:
ClassValueSet- Simplifyequals()andhashCode()- Removed redundant second iteration in
equals()—size()check + one-direction subset check is sufficient - Removed dead
h += 0null branch and unnecessary null guard inhashCode()
- Removed redundant second iteration in
- PERFORMANCE:
CompactSet- Addequals()self-check to avoid O(n) comparison when comparing to self - BUG FIX:
ClassValueMap-remove(null, value)andreplace(null, oldValue, newValue)use identity comparison instead ofequals()- Both methods used
AtomicReference.compareAndSet()which compares with==, notequals() - For non-interned objects, the operations silently failed even when the value was logically equal
- Fixed: CAS loops with
Objects.equals()for proper value-based comparison per theConcurrentMapcontract
- Both methods used
- PERFORMANCE:
ClassValueMap- CacheentrySet()andvalues()view objects- Previously created new anonymous instances on every call; now cached in
transientfields
- Previously created new anonymous instances on every call; now cached in
- PERFORMANCE:
ClassValueMap- OverridecontainsValue()for direct check- Previously inherited
AbstractMap.containsValue()which iteratesentrySet()creating wrapper objects - Now checks
nullKeyStore+backingMap.containsValue()directly without allocation
- Previously inherited
- PERFORMANCE:
ClassValueMap- OverrideisEmpty()for short-circuitbackingMap.isEmpty()is O(1) vs inheritedsize() == 0which computes full ConcurrentHashMap size
- BUG FIX:
ConcurrentNavigableMapNullSafe-wrapEntry().getValue()performs live lookup instead of returning snapshot value- Navigation entry methods (
firstEntry,lastEntry,lowerEntry,floorEntry,ceilingEntry,higherEntry) returned entries whosegetValue()didinternalMap.get(key)— a live lookup - Violated the
NavigableMapcontract that these methods return snapshot entries - If the map was modified after getting the entry,
getValue()returned the new value ornull(if the key was removed) - Fixed:
wrapEntry()now returnsSimpleImmutableEntrywith snapshot value, matchingConcurrentSkipListMapbehavior
- Navigation entry methods (
- PERFORMANCE:
ConcurrentNavigableMapNullSafe- CachekeySet()view object- Previously created a new
KeyNavigableSeton everykeySet()call, bypassing the base class cache - Now cached in a
transientfield; the view is already backed by the live internal map
- Previously created a new
- BUG FIX:
ConcurrentList-remove(int)TOCTOU race can remove wrong elementindex == size() - 1check was before the write lock; concurrent modifications between the check andremoveLast()acquiring the lock could cause a different element to be removed- Fixed: moved size check inside the write lock
- BUG FIX:
ConcurrentList-add(int, E)TOCTOU race can insert at wrong positionindex == size()check was before the write lock; concurrentaddLast()between the check and lock acquisition could place the element at the wrong index- Fixed: moved size check inside the write lock
- BUG FIX:
ConcurrentList-addAll(Collection)not atomic — elements can be interleaved- Each
addLast()acquired/released the write lock individually, allowing concurrent operations to interleave elements within the batch - Inconsistent with
addAll(int, Collection)which was already atomic - Fixed: write lock is now held for the entire operation
- Each
- PERFORMANCE:
ConcurrentList-hashCode(),equals(),forEach(), andtoString()avoid O(n) snapshot allocation- Previously created snapshot arrays via
iterator()→toArray(); now iterate directly under read lock
- Previously created snapshot arrays via
- BUG FIX:
Converter-isConversionSupportedFor()cachesUNSUPPORTEDfor user-added conversions, poisoningconvert()getConversionFromDBs()used hardcoded instanceId0LforUSER_DBlookups, butaddConversion()stores entries withthis.instanceIdisConversionSupportedFor()cachedUNSUPPORTEDwhen it couldn't find the user conversion- If
isConversionSupportedFor(source, target)was called beforeconvert(source, target), theUNSUPPORTEDentry poisoned the cache, causingconvert()to silently returnnull - Fixed:
getConversionFromDBs()now checksUSER_DBwiththis.instanceIdfirst
- BUG FIX:
Converter-hasConverterOverrideFor()misses dynamically added conversions- Extracted instanceId from
options.getConverterOverrides()iterator instead of usingthis.instanceId - Conversions added via
addConversion()after construction were invisible toisSimpleTypeConversionSupported() - Fixed: uses
this.instanceIddirectly for a simple O(1) hash lookup
- Extracted instanceId from
- BUG FIX:
Converter- Constructor stores option overrides with wrong instanceIdConverterOptionsoverrides were stored inUSER_DBwith the pair's instanceId (typically0L) instead ofthis.instanceId- This was inconsistent with
addConversion()which usesthis.instanceId, causing lookup mismatches - Fixed: constructor now stores overrides with
this.instanceId
- BUG FIX:
Converter-isConversionSupportedFor(Class)static cache ignores dynamic overridesSELF_CONVERSION_CACHEis static but caches results from instance methods that depend on instance-specific state- If an instance without overrides cached
false, instances with user overrides also gotfalse - Fixed: checks
hasConverterOverrideFor()before consulting the static cache
- PERFORMANCE:
Converter- SkipUSER_DBlookup inconvert()when no user conversions exist- Added
hasUserConversionsflag set in constructor andaddConversion() - When
false, theUSER_DB.get()call is skipped, saving oneConversionPairallocation +ConcurrentHashMap.get()perconvert()call
- Added
- PERFORMANCE:
Converter- Remove redundant instance-level caching for built-in conversions- Built-in conversions were cached at both
0L(shared) andinstanceId(instance-specific) - The instance-level cache was redundant since
getCachedConverter()falls back to the shared0Lentry - Removed the redundant
cacheConverter()call, reducingFULL_CONVERSION_CACHEentries
- Built-in conversions were cached at both
- BUG FIX:
Converter-isConversionSupportedFor()andisSimpleTypeConversionSupported()miss user conversions via inheritance- Both methods called
getInheritedConverter()with hardcoded instanceId0Linstead ofthis.instanceId - If a user registered a conversion for a parent class (e.g.,
Number→MyType), queries for child classes (e.g.,Integer→MyType) could not find it via the inheritance walk - Worse, both methods cached
UNSUPPORTED, poisoning theconvert()cache — the same class of cache poisoning bug as the direct-lookup fix above, but in the inheritance path - Fixed: both methods now use
this.instanceIdfor the inheritance walk, consistent withconvert()
- Both methods called
- CLEANUP:
Converter- Removed dead code ingetConversionFromDBs()- After the constructor fix (storing overrides at
this.instanceId), theUSER_DBcheck at instanceId0Lwas unreachable dead code
- After the constructor fix (storing overrides at
- BUG FIX:
ConcurrentSet-toString()used{}braces instead of standard[]brackets- All JDK
Setimplementations andAbstractCollection.toString()use[];ConcurrentSetwas the only outlier
- All JDK
- PERFORMANCE:
ConcurrentSet-retainAll()usesHashSetinstead ofConcurrentHashMap.newKeySet()for temporary lookup- The temporary wrapped-collection set is only accessed by the current thread; concurrent overhead was unnecessary
- MAINTENANCE:
CaseInsensitiveSet- Added regression tests for case-insensitivehashCode()andretainAll()- Verified
hashCode()is case-insensitive throughSetFromMap→CaseInsensitiveMap.keySet().hashCode()delegation - Verified
retainAll()is case-insensitive throughSetFromMap→CaseInsensitiveMap.keySet().retainAll()delegation
- Verified
- BUG FIX:
CompactMap- EMPTY_MAP sentinel collision causing silent data loss- The
EMPTY_MAPsentinel was a staticString; if a user stored that exact string as a value, Java string interning caused the map to appear empty - Fixed: changed
EMPTY_MAPto a uniqueObjectinstance that cannot collide with user values
- The
- BUG FIX:
CompactMap- Iteratorremove()during Map-to-array transition- When
iterator.remove()caused size to drop tocompactSize(), the code bypassedmapIterator.remove()and calledCompactMap.this.remove()directly, creating fragile coupling to backing map iteration order - Fixed: always use
mapIterator.remove()first, then manually build the compact array from remaining entries
- When
- BUG FIX:
CompactMap-equals()missingreturn truefor compact array path- The loop verifying all entries match fell through without returning
true, reaching a fallthrough path that redundantly checked equality viaentrySet().equals() - Fixed: added
return trueafter the compact array verification loop
- The loop verifying all entries match fell through without returning
- BUG FIX:
CompactMap-removeFromMap()not sorting array after Map-to-array transition- Entries were copied in backing map's iteration order without sorting; for sorted/reverse CompactMaps, binary search failed to find present keys
- Fixed: call
sortCompactArray()after building the array in bothremoveFromMap()and the iterator's transition path
- BUG FIX:
CompactMap-keySet().retainAll()case-sensitivity mismatch- For legacy subclasses where
isCaseInsensitive()returnstruebutgetNewMap()returns a plainHashMap, the retain lookup was case-sensitive - Keys differing only in case from the retain collection were incorrectly removed
- Fixed: use
CaseInsensitiveMapfor the lookup whenisCaseInsensitive()is true
- For legacy subclasses where
- BUG FIX:
CompactMap-ConcurrentModificationExceptiondetection uses size, not modCount- Iterator used
expectedSize != size()to detect concurrent modification, missing structural changes where size stayed constant (e.g., add one key + remove another) - Fixed: added
modCountfield that increments on every structural modification; iterator checksexpectedModCountmatching standard Java collection behavior
- Iterator used
- PERFORMANCE:
CompactMap- Eliminate double lookup inremoveFromMap()- Replaced
containsKey()+remove()with singleremove()call, falling back tocontainsKey()only whenremove()returns null
- Replaced
- PERFORMANCE:
CompactMap- OptimizehandleTransitionToSingleEntry()- Directly assigns
valto remaining entry, bypassingclear()+put()which dispatches through the full state chain
- Directly assigns
- PERFORMANCE:
CompactMap- Single scan inequals()for compact array path- Replaced
containsKey()+get()(two linear scans per entry) with single inline scan that finds key and retrieves value in one pass
- Replaced
- PERFORMANCE:
CompactMap- Cache size in iterator- Replaced
size()calls inhasNext()andadvance()with the already-trackedexpectedSizefield, avoidinginstanceofdispatch on every iteration step
- Replaced
4.90.0 - 2026-02-02
- BUG FIX:
DeepEquals- URL comparison now uses string representation instead ofURL.equals()- Java's
URL.equals()performs DNS resolution which causes flaky CI failures - Now compares URLs using
toExternalForm()for reliable, deterministic comparison
- Java's
- MAINTENANCE: Migrated test files from deprecated
JsonIo.toObjects()toJsonIo.toJava().asClass()API- Updated 8 calls in
CompactMapTestandConverterEverythingTest
- Updated 8 calls in
- PERFORMANCE: Added
FastReader.readUntil()for bulk character reading until delimiter- Reads characters into destination buffer until one of two delimiters is found
- Delimiter character is left unconsumed for subsequent read
- Enables bulk string parsing optimization in json-io's JsonParser
4.89.0 - 2026-01-31
- PERFORMANCE:
FastReader.getLastSnippet()now returns bounded 200-char context- Previously could return 0 to 8192 characters depending on buffer position
- Now consistently returns up to the last 200 characters read for useful error context
- PERFORMANCE:
ParameterizedTypeImpl.getActualTypeArguments()removed defensive clone- Returns direct reference for performance - callers should not modify the array
- Eliminates allocation overhead in hot paths during type resolution
- PERFORMANCE:
Converter.isConversionSupportedFor(source, target)now caches negative results- Previously only cached positive hits, causing repeated inheritance traversal for unsupported pairs
- Now caches
UNSUPPORTEDsentinel for O(1) lookup on subsequent calls addConversion()already clears caches viaclearCachesForType(), so invalidation is handled
- PERFORMANCE:
ReflectionUtils.getMethod()andgetNonOverloadedMethod()now cache negative results- Previously threw exceptions inside
computeIfAbsent, bypassing the cache for "not found" cases - Now caches sentinel
Methodobjects for failed lookups, achieving O(1) on subsequent calls - Avoids repeated expensive class hierarchy traversals for non-existent methods
- Previously threw exceptions inside
- PERFORMANCE:
ConcurrentSet.spliterator()now returns an optimized spliterator for parallel streams- Delegates to underlying
ConcurrentHashMapspliterator for efficient parallel decomposition - Properly reports
CONCURRENTandDISTINCTcharacteristics - Correctly handles null sentinel unwrapping for null element support
- Enables efficient
parallelStream()operations onConcurrentSet
- Delegates to underlying
- BUG FIX:
TrackingMap.putIfAbsent()now correctly handles null values- Previously used
get() == nullwhich conflates "key absent" with "key present with null value" - Now uses
containsKey()to properly distinguish between the two cases
- Previously used
- BUG FIX:
TrackingMap.computeIfPresent()now only tracks keys that were actually present- Previously tracked the key unconditionally, even when the key didn't exist
- PERFORMANCE:
TrackingMapnow caches interface cast results at construction time- Avoids repeated
instanceofchecks and casts forasConcurrent(),asNavigable(),asSorted()methods
- Avoids repeated
- PERFORMANCE:
TrackingMap- Pre-sizedHashSetin constructor forreadKeys- Uses initial capacity of 16 to reduce early rehashing
- BUG FIX:
TrackingMapsub-maps now share the parent'sreadKeyssetsubMap(),headMap(),tailMap(),descendingMap()now correctly track reads across all views- Previously each sub-map had an isolated
readKeysset that didn't reflect in parent
- BUG FIX:
TTLCache.containsValue()now filters expired entries- Previously returned
truefor expired entries still in the cache
- Previously returned
- BUG FIX:
TTLCache.keySet()now filters expired entries- Iterator skips expired entries and correctly removes underlying cache entries
- BUG FIX:
TTLCache.values()now filters expired entries- Iterator skips expired entries and correctly removes underlying cache entries
- BUG FIX:
TTLCache.entrySet()now filters expired entries- Iterator skips expired entries with proper look-ahead logic
remove()correctly removes the last-returned entry, not the prefetched next entry
- BUG FIX:
TTLCache.hashCode()now filters expired entries- Previously included expired entries in hash computation
- BUG FIX:
TTLCache.close()now thread-safe- Added
closedvolatile flag to prevent double-close of scheduled executor
- Added
- BUG FIX:
TestUtil.assertContainsIgnoreCase()andcheckContainsIgnoreCase()now correctly advance past found tokens- Previously could match the same token multiple times when searching for sequential occurrences
- Now uses
indexOf(needle, fromIndex)to ensure each token is found after the previous one
- BUG FIX:
TestUtil.fetchResource()now uses UTF-8 charset as documented- Previously used platform default charset despite Javadoc stating UTF-8
- BUG FIX:
TestUtil.fetchResource()now throws descriptive error for missing resources- Previously threw NPE with no context when resource not found
- Now throws
IllegalArgumentExceptionwith resource name
- PERFORMANCE:
TestUtil.assertContainsIgnoreCase()andcheckContainsIgnoreCase()avoid substring allocation- Uses offset tracking with
indexOf(needle, fromIndex)instead of creating substring on each iteration
- Uses offset tracking with
- PERFORMANCE:
Traverseroptimizations for object graph traversal- Added
ClassValueMapcache forshouldSkipClass()to avoid repeatedisAssignableFrom()checks - Hoisted security limit lookups outside traversal loop (limits don't change during traversal)
NodeVisitconstructor now wraps maps directly instead of creating unnecessaryHashMapcopies- Inlined validation methods to reduce method call overhead
- Added
- PERFORMANCE:
SafeSimpleDateFormatthread-safety and allocation improvementsStateconstructor now takesLonginstead ofDateto avoid temporary object allocationgetSdf()now usescomputeIfAbsent()instead of manual get/put patternupdate()method now usescompareAndSetloop for proper thread-safety under concurrent mutations- Eliminated unnecessary
Dateobject creation in setter methods
- CLEANUP: Import organization and unused import removal across multiple classes
4.88.0 - 2026-01-26
- BUG FIX:
FastReader- Added bounds validation inread(char[], int, int)method- Now throws
IndexOutOfBoundsExceptionfor invalid offset, length, or buffer overflow - Matches standard
Readercontract andFastWriterbehavior
- Now throws
- BUG FIX:
FastWriter- Fixed NPE inflush()afterclose()flush()now returns safely when called afterclose()instead of throwingNullPointerException- Matches
close()behavior which already handles this case
- BUG FIX:
FastWriter- Added bounds validation inwrite(String, int, int)method- Now throws
IndexOutOfBoundsExceptionfor invalid offset/length parameters - Matches validation in
write(char[], int, int)method
- Now throws
- PERFORMANCE:
FastWriter- Improved buffer utilization inwrite(int c)method- Now uses full buffer capacity before flushing (was wasting one slot)
- Buffer is flushed immediately when full to maintain invariants for other write methods
- PERFORMANCE:
FastWriter- Made classfinalfor JVM optimizations- Enables JIT compiler to inline method calls, matching
FastReaderwhich is already final
- Enables JIT compiler to inline method calls, matching
- BUG FIX:
FastByteArrayOutputStream- Fixed critical integer overflow ingrow()method - NEW:
FastByteArrayInputStream- Added JDK 9+ compatible methodsreadAllBytes()- Efficient single-copy implementation (auto-overrides on JDK 9+)readNBytes(int len)- Efficient partial read (auto-overrides on JDK 11+)transferTo(OutputStream)- Single write operation to output (auto-overrides on JDK 9+)- All methods work on JDK 8 as regular methods and automatically become overrides on newer JDKs
- NEW:
FastByteArrayOutputStream- Added zero-copy buffer access methodsgetInternalBuffer()- Direct access to internal buffer without copyinggetCount()- Returns valid byte count for use withgetInternalBuffer()toInputStream()- CreatesFastByteArrayInputStreamfrom current data
- NEW:
FastByteArrayOutputStream- AddedtoString(Charset)method- Allows explicit charset specification instead of platform default encoding
- CLEANUP:
FastByteArrayInputStream- Added missing@Overrideonread()method - CLEANUP:
FastByteArrayInputStream- Added explicit(int)cast inskip()method- Makes the safe long-to-int conversion explicit with explanatory comment
- CLEANUP:
FastByteArrayOutputStream- Fixed Javadoc typo ("theoerical" → "theoretical") - PERFORMANCE:
FastByteArrayOutputStream- Added early return optimization write(byte[], int, int)now returns immediately whenlen == 0- Skips unnecessary bounds checks and capacity operations for zero-length writes
- Previous 2x growth (
oldCapacity << 1) overflowed for buffers > 1GB causingNegativeArraySizeException - Changed to 1.5x growth strategy (
oldCapacity + (oldCapacity >> 1)) to reduce overflow risk - Added
MAX_ARRAY_SIZEconstant (Integer.MAX_VALUE - 8) following JDK best practices - Added
hugeCapacity()method for safe handling of very large allocations - BUG FIX:
FastByteArrayOutputStream- Fixed inconsistent null exception typewrite(byte[], int, int)now throwsNullPointerExceptionfor null array (wasIndexOutOfBoundsException)- Matches JDK convention and
FastByteArrayInputStreambehavior
- BUG FIX:
FastByteArrayInputStream- Added null validation in constructor- Constructor now throws
NullPointerExceptionwith descriptive message for null input - Previously threw
NullPointerExceptiononbuf.lengthaccess with no message
- Constructor now throws
- BUG FIX:
StringUtilities- FixedcommaSeparatedStringToSet()return type inconsistency- Changed from
Collectors.toSet()toCollectors.toCollection(LinkedHashSet::new) - Now consistently returns
LinkedHashSetas documented, maintaining insertion order
- Changed from
- BUG FIX:
StringUtilities- Fixed integer overflow inrepeat()method- Moved overflow check outside security block so it always runs
- Prevents
StringBuilderfrom being created with negative capacity whens.length() * countoverflows
- BUG FIX:
StringUtilities- Fixed integer overflow inencode()method- Added overflow check for
bytes.length << 1to handle very large byte arrays - Added null check returning
nullfor consistency withdecode()method
- Added overflow check for
- PERFORMANCE:
FastReader- OptimizedgetLastSnippet()implementation- Replaced character-by-character
StringBuilder.append()loop withnew String(buf, 0, position)
- Replaced character-by-character
- PERFORMANCE:
StringUtilities- OptimizedsnakeToCamel()to avoid array allocation- Replaced
toCharArray()iteration withcharAt()loop - Added
StringBuilderinitial capacity hint
- Replaced
- PERFORMANCE:
StringUtilities- OptimizedtrimEmptyToDefault()to avoid allocation- Replaced
Optional.ofNullable().map().orElse()with simple null check - Removed unused
Optionalimport
- Replaced
- PERFORMANCE:
StringUtilities- Optimizedrepeat()with O(log n) doubling algorithm- Replaced O(n) loop with doubling algorithm for large repeat counts
- Added early return for empty string input
- PERFORMANCE:
StringUtilities- OptimizedpadLeft()andpadRight()methods- Replaced character-by-character loop with
Arrays.fill()on char array
- Replaced character-by-character loop with
- PERFORMANCE:
StringUtilities- Optimizedencode()hex encoding- Replaced
StringBuilderwith direct char array manipulation - Accesses
HEX_ARRAYdirectly instead of callingconvertDigit()method
- Replaced
- CLEANUP:
FastReader- Fixed misleading constructor error message- Changed "Buffer sizes must be positive" to accurately state that
bufferSizemust be positive andpushbackBufferSizemust be non-negative
- Changed "Buffer sizes must be positive" to accurately state that
- BUG FIX:
UniqueIdGenerator- Added validation togetDate()andgetDate19()methods- Both methods now throw
IllegalArgumentExceptionfor negative IDs - Matches existing validation in
getInstant()andgetInstant19()
- Both methods now throw
- PERFORMANCE:
UniqueIdGenerator- Replaced reflection with MethodHandle foronSpinWait()- Changed from
Method.invoke()toMethodHandle.invokeExact()in spin-wait loop MethodHandle.invokeExact()can be inlined by JIT compiler;Method.invoke()cannot- Estimated 10-50x faster spin-wait hint delivery after JIT warmup
- Changed from
- PERFORMANCE:
UniqueIdGenerator- OptimizedwaitForNextMillis()to reduce syscall overhead- Reduced
currentTimeMillis()calls by batching 8 spin-waits between time checks - Previously called
currentTimeMillis()after every singleonSpinWait()
- Reduced
- PERFORMANCE:
UniqueIdGenerator- Optimized static initialization- Cached hostname lookup to avoid duplicate
getExternalVariable("HOSTNAME")calls - Replaced SHA256 hash with simpler
String.hashCode()for hostname-based server ID - Both provide sufficient distribution for 0-99 range; hashCode is much faster
- Cached hostname lookup to avoid duplicate
- CLEANUP:
UniqueIdGenerator- Removed unusedStandardCharsetsimport- No longer needed after switching from SHA256 to hashCode for hostname hashing
- PERFORMANCE:
ReflectionUtils- Cache parsed dangerous class and sensitive field patterns- Added volatile caching with property change detection for
getDangerousClassPatterns()andgetSensitiveFieldPatterns() - Patterns are now parsed once and cached until the system property value changes
- Pre-processes patterns (trim/toLowerCase) during cache population for efficient matching
- Reduces repeated string parsing overhead in security checks
- Added volatile caching with property change detection for
- PERFORMANCE:
ReflectionUtils- Cache Method lookups for Record component operations- Extended
RecordSupportclass to cacheRecordComponent.getName()andRecordComponent.getAccessor()methods - Avoids repeated reflection lookups when processing Record types
- Extended
- SECURITY:
ReflectionUtils- Add bounds checking ingetClassNameFromByteCode()- Added validation for
this_classindex and string pool index before array access - Prevents
ArrayIndexOutOfBoundsExceptionon malformed class files - Returns clear
IllegalStateExceptionwith descriptive message for invalid bytecode
- Added validation for
- CLEANUP:
ReflectionUtils- Removed dead codemakeParamKey()method- Method was unused and has been removed
4.87.0 - 2026-01-26
- SECURITY:
DateUtilities- Fixed ReDoS vulnerability in malformed input validation- Replaced vulnerable regex pattern
(.{10,})\1{4,}with algorithmichasExcessiveRepetition()method - New method detects excessive repetition without catastrophic backtracking risk
- Replaced vulnerable regex pattern
- PERFORMANCE:
DateUtilities- Multiple optimizations reducing memory allocation- Replaced
ConcurrentHashMapwithHashMapfor months and timezone maps (immutable data) - Replaced
BigDecimalarithmetic inconvertFractionToNanos()with string/long operations - Removed unused
BigDecimalimport
- Replaced
- IMPROVED:
DateUtilities- Updated timezone nameEurope/KievtoEurope/Kyiv- Reflects modern IANA timezone database naming (changed in 2022)
- CLEANUP:
ClassUtilities- Removed dead code and unnecessary synchronization- Removed unused
ARG_PATTERNfield (pre-compiled regex that was never used) - Removed unnecessary inner
synchronized(holder)blocks in alias methods - Outer synchronization on
ALIASES_TO_CLASSalready provides thread safety
- Removed unused
- BUG FIX:
ConcurrentList- Fixed null element handling in Deque methodsgetFirst(),getLast(),removeFirst(),removeLast()now correctly handle null elements- Previously threw
NoSuchElementExceptionwhen list contained null as a valid element
- BUG FIX:
ConcurrentList- Fixed visibility race condition in addFirst/addLast- Changed from
lazySet()toset()for immediate visibility - Changed from read lock to write lock to prevent readers seeing updated counters before values written
- Changed from
- BUG FIX:
ConcurrentList- Fixed hashCode to comply with List contract- Removed
EncryptionUtilities.finalizeHash()which violated the standard List.hashCode() formula - ConcurrentList.hashCode() now matches ArrayList.hashCode() for equal lists
- Removed
- BUG FIX:
ConcurrentList- Added bucket cleanup to prevent memory leak- Empty buckets outside head/tail range are now removed after poll operations
- Prevents unbounded memory growth during heavy addFirst/pollFirst or addLast/pollLast usage
- PERFORMANCE:
ConcurrentList- Multiple optimizations reducing allocationsiterator()now uses direct array-backed iterator instead of ArrayList intermediarylistIterator()now uses direct array-backed ListIteratortoArray()builds result array directly instead of via ArrayListcontains(),indexOf(),lastIndexOf()use direct index access instead of iterator- Simplified
getBucket()to direct lookup (buckets always exist for valid indices)
- BUG FIX:
EncryptionUtilities- Fixed NumberFormatException on malformed salt/IV size properties- Created
getMinSaltSize(),getMaxSaltSize(),getMinIvSize(),getMaxIvSize()helper methods - These methods handle NumberFormatException gracefully like existing property getters
- Previously, invalid property values caused uncaught exceptions in encrypt/decrypt operations
- Created
- SECURITY:
EncryptionUtilities- Clear PBEKeySpec password from memory after key derivation- Added
finallyblock to callspec.clearPassword()afterderiveKey()operations - Follows security best practice to minimize password exposure in memory
- Added
- IMPROVED:
EncryptionUtilities- Replaced magic numbers in decrypt methods with constants- Decrypt methods now use
STANDARD_SALT_SIZEandSTANDARD_IV_SIZEto compute offsets - Makes code self-documenting and prevents silent breakage if constants change
- Decrypt methods now use
- PERFORMANCE:
EncryptionUtilities- Multiple optimizations reducing allocations- Cached
SecureRandominstance as static final field (thread-safe, avoids repeated instantiation) - Cached
SecretKeyFactoryinstance for PBKDF2 (thread-safe, avoidsgetInstance()per call) - Use
ByteBufferfor cleaner output assembly in encrypt methods
- Cached
- CLEANUP:
EncryptionUtilities- Removed system property pollution in static initializer- Previously set default values into global System.properties namespace
- Getter methods already handle missing properties by returning defaults
- BUILD: Version alignment with json-io 4.87.0
- Keeping java-util and json-io version numbers synchronized simplifies dependency management and ensures compatibility
4.86.0 - 2025-01-26
- PERFORMANCE:
CompactMap- Multiple optimizations reducing memory allocation and improving iteration performance- Fixed redundant
get()call inentrySet().contains()- now reuses the result instead of calling bothget()andcontainsKey() - Use
getCachedLegacyConstructed()consistently inswitchBackingFromMapToArray()instead of callinggetClass().getName().endsWith() - Cache
CompactMapComparatorinstances statically - 4 pre-created comparators for all combinations of case-insensitive and reverse ordering - Added
isArraySorted()check inquickSort()to skip unnecessary sorting during iteration (common case since binary insertion input()maintains order)
- Fixed redundant
4.85.0 - 2025-01-24
- PERFORMANCE:
ClassUtilities- Lock-free cache operations using CAS-based removal- Replaced synchronized blocks in
fromCache(),toCache(), anddrainQueue()withConcurrentHashMap.remove(key, value) - CAS-based removal only removes if the exact WeakReference is still present, preventing race conditions
- Eliminates lock contention in high-throughput class loading scenarios
- Replaced synchronized blocks in
- PERFORMANCE:
ClassUtilities- Added cache for assignable type lookups ingetArgForType()- New
ASSIGNABLE_TYPE_CACHEusingClassValueMap<Optional<Supplier<Object>>>avoids repeated O(n) scans ofASSIGNABLE_CLASS_MAPPING - Uses
Optionalto distinguish "no match found" (Optional.empty()) from "not yet cached" (null) - Improves constructor parameter resolution performance for repeated type lookups
- New
- PERFORMANCE:
ClassUtilities- Optimized synthetic parameter name detection- Added
isSyntheticArgName()method using efficient string operations instead of regex - Avoids
Pattern.matcher().matches()overhead for checking "arg0", "arg1", etc. patterns - Used in constructor parameter resolution to detect compiler-generated names
- Added
- PERFORMANCE: `DeepEquals$ - \text{Replace} \text{O}(\text{n}²) \text{visited} \text{set} \text{copying} \text{with} \text{O}(1) \text{ScopedSet} \text{for} \text{unordered} \text{collections}
- \text{Previously}, \text{comparing} \text{unordered} \text{collections} \text{copied} \text{the} \text{entire} \text{visited} \text{set} \text{for} \text{each} \text{probe} \text{element} (\text{O}(\text{n} \times \text{k}))
- \text{New} $ScopedSet` wrapper creates a lightweight view with O(1) creation time
- Tracks local additions separately and discards them when probe completes
- Significant improvement for comparing large unordered collections/maps with cycles
- PERFORMANCE:
DeepEquals- Multiple optimizations reducing per-comparison overhead- Replaced
ConcurrentSetwithHashSetfor visited tracking - no concurrent access occurs - Cache system properties at class load time instead of parsing on every call
- Simplified
hashCode()by removing unnecessaryfinalizeHash()call
- Replaced
- IMPROVED:
ConcurrentNavigableSetNullSafe- Changed sentinel from String to Object instance- Previous
"null_" + UUID.randomUUID()String could theoretically collide with user data - New
new Object()sentinel is guaranteed unique and cannot collide - Also changed from
.equals()to identity comparison (==) for sentinel detection
- Previous
- BUG FIX:
ClassValueMap- Fixedclear()race condition that could leave permanently stale cache entries- Previous order: invalidate cache → clear backingMap (allowed concurrent
get()to repopulate cache from still-populated backingMap) - Fixed order: snapshot keys → clear backingMap → invalidate cache (ensures any
computeValueafter clear sees empty map) - After
clear()completes, all subsequentget()calls now see correct state
- Previous order: invalidate cache → clear backingMap (allowed concurrent
- BUG FIX:
ConcurrentNavigableMapNullSafe- FixedClassCastExceptionon sub-map views- Navigational methods (
lowerKey,floorKey,ceilingKey,higherKey, etc.) incorrectly cast toConcurrentSkipListMap - Sub-map, head-map, tail-map, and descending-map views are not
ConcurrentSkipListMapinstances - Fixed by casting to
ConcurrentNavigableMapinstead (the interface that defines these methods)
- Navigational methods (
- BUG FIX:
ConcurrentNavigableSetNullSafe- Fixed user comparators that don't handle nulls throwing NPE- Comparators like
String.CASE_INSENSITIVE_ORDERwould throw NPE when comparing null elements - Wrapper now gracefully falls back to default null ordering (nulls > non-nulls) if comparator throws NPE
- User comparators that handle nulls (via
Comparator.nullsFirst/nullsLastor custom handling) still control null ordering
- Comparators like
- BUILD: Updated json-io test dependency from 4.83.0 to 4.84.0
4.84.0 - 2025-01-19
- BUG FIX:
ClassValueMap- Fixed race condition inputIfAbsent(null, value)for null key handling- Previously used separate
volatile boolean hasNullKeyMapping+AtomicReference<V> nullKeyValuefields - Race: thread A could see
hasNullKeyMapping=truebut read stalenullKeyValuebefore thread B's write completed - Fix: Combined into single
AtomicReference<Object> nullKeyStorewith sentinel values (NO_NULL_KEY_MAPPING,NULL_FOR_NULL_KEY) - Eliminates volatile read on common
get()path - now pure AtomicReference.get()
- Previously used separate
- BUG FIX:
TTLCache- Fixed thread-safety issue input()that could corrupt LRU chain- Previously used
tryLock()and skipped LRU updates if lock unavailable - Under contention, cache map and LRU chain could become inconsistent
- Fix: Now acquires lock for entire put operation; moves cache map update inside lock
- Added safety check in
moveToTail()to skip already-evicted nodes
- Previously used
- BUG FIX:
UniversalConversions- Fixed NPE in bridge methods when source value is nullintegerToAtomicInteger(),longToAtomicLong(),booleanToAtomicBoolean()now return null for null input- Previously threw NullPointerException on
new AtomicXxx(null)
- PERFORMANCE:
LRUCache(THREADED strategy) - Removed unnecessary volatile fromNode.valueandNode.timestampvalueis never modified after constructiontimestampis used for approximate LRU only - stale reads acceptable given existing approximations- ConcurrentHashMap publish provides necessary memory barriers for initial visibility
- PERFORMANCE:
LRUCache(LOCKING strategy) - GC improvements- Made
Node.keyfinal - Null out
node.prev/node.nextlinks on removal to avoid GC nepotism - Null out
node.valueon eviction to release value reference promptly
- Made
- PERFORMANCE:
StringUtilities.hashCodeIgnoreCase()- Single-pass algorithm optimization- Previously made two passes: first to check ASCII, then to compute hash
- Now computes hash while checking for non-ASCII, falling back only when non-ASCII detected
- PERFORMANCE:
Converter- Removed ~50 redundant conversion entries now handled by surrogate system- Entries like
Byte → AtomicBoolean,String → AtomicInteger, etc. are now automatic via surrogate bridges - Example:
String → AtomicIntegernow goesString → Integer → AtomicIntegervia surrogate system - Reduces CONVERSION_DB size and maintenance burden without losing any functionality
- Entries like
- NEW:
Converter- AddedBoolean ↔ BitSetconversionsBitSet → Boolean: returnstrueif any bit set,falseif emptyBoolean → BitSet:truecreates BitSet with bit 0 set,falsecreates empty BitSet- Primitive
boolean ↔ BitSetworks automatically via surrogate system
- IMPROVED:
LRUCache(THREADED strategy) - Fixed scheduler shutdown to properly clear referenceshutdownScheduler()now setsscheduler = nullafter shutdown to allow recreation- Previously, shutdown scheduler reference remained, preventing new cache instances from starting cleanup
- BUILD: Updated json-io test dependency from 4.81.0 to 4.83.0
- BUILD: Re-enabled
ConverterEverythingTest(was temporarily excluded) - DOCS: Updated README to reflect 1600+ conversions (was 1000+)
4.83.0 - 2025-01-18
- PERFORMANCE:
LRUCache(THREADED strategy) - Major performance improvements reducing overhead from ~5x to ~1.4x vs ConcurrentHashMap- Replaced
System.nanoTime()with logical clock (AtomicLong counter) for LRU ordering - ~5ns vs ~25ns per operation - Simplified probabilistic timestamp updates using timestamp low bits - eliminates atomic/ThreadLocal operations on most accesses
- Removed extra
get()call input()since Node creation is now cheap with logical clock - Added amortized eviction (batch every 16 inserts) to spread eviction cost across operations
- Replaced
- BUG FIX:
LRUCache- Fixed hard cap enforcement to guarantee memory bounds under high-throughput scenarios- Split eviction into
tryEvict()(skippable) andforceEvict()(blocks until complete) - Hard cap (2x capacity) now uses
LockSupport.parkNanos(1000)for efficient spinning with low CPU usage - Fixes issue where rapid inserts could exceed memory bounds when eviction couldn't keep up
- Split eviction into
- CHANGE:
CaseInsensitiveMap- Default cache changed fromConcurrentHashMaptoLRUCache(THREADED strategy)- Provides true LRU eviction (hot entries preserved) vs random eviction with ConcurrentHashMap
- Guarantees bounded memory (max 2x capacity) vs loose bounds with ConcurrentHashMap
- Performance is equivalent or better in benchmarks
- Users can still use
replaceCache()to configure ConcurrentHashMap if desired
4.82.0 - 2025-01-17
- BUG FIX:
TypeUtilities- Fixed WildcardType bounds array mutation bug- External
WildcardTypeimplementations return internal arrays fromgetUpperBounds()/getLowerBounds() - These arrays were being modified in-place during resolution, corrupting the original type
- Fix: Clone arrays from external implementations before modification; skip cloning for internal
WildcardTypeImpl
- External
- BUG FIX:
TypeUtilities- Fixed unsafe cast for method-level TypeVariablesTypeVariablecan be declared onMethodorConstructor, not justClass- Casting
getGenericDeclaration()toClasswould throwClassCastExceptionfor method-level type variables - Fix: Check declaration type and return first bound for non-class type variables
- PERFORMANCE:
TypeUtilities- Added array class cache to avoid repeatedArray.newInstance()allocations ingetRawClass() - PERFORMANCE:
TypeUtilities- UseIdentitySetinstead ofHashSetfor cycle detection (reference equality is sufficient and faster) - IMPROVED:
TypeUtilities- SimplifiedhashCode()implementations using standard31 * resultpattern instead ofEncryptionUtilities.finalizeHash() - IMPROVED:
TypeUtilities.WildcardTypeImpl- Constructor now takes ownership of arrays without cloning (internal class only) - IMPROVED:
DeepEquals- Fixed Javadoc formatting using{@code}blocks for proper HTML rendering of generic types - DEPRECATED:
LRUCache(int capacity, int cleanupDelayMillis)- ThecleanupDelayMillisparameter is no longer used; useLRUCache(int)instead - BUILD: Added
@SuppressWarningsannotations to eliminate compile warnings across multiple classes:CaseInsensitiveMap,ClassUtilities,ConcurrentNavigableSetNullSafe,ConcurrentSetMultiKeyMap,ReflectionUtils,TTLCache,UrlUtilities,MapConversions
- BUILD: Removed unused regex timeout methods from
DateUtilities - BUILD: Cleaned up unused code in
ObjectConversions - PERFORMANCE:
MultiKeyMap- Significant hot path optimizations using Class identity checksvalueHashCode()- Reordered type checks to handle Integer/Long/Double first; uses Class identity (==) instead ofinstanceoffor common types (55% CPU reduction in JFR profiling)computeElementHash()- Added Class identity fast path for String/Integer/Long/Double, skipping 4+ instanceof checks for 75% of typical key elementsflattenKey()- Added fast path at method entry for String/Integer/Long/Double keysflattenObjectArrayN()- Skip array/collection checks for known simple typesisArrayOrCollection()- Added Class identity short-circuit for common simple types- Result: Cedar MultiKeyMap win rate vs Apache Commons Collections improved from ~60% to ~71% in performance benchmarks, while providing thread-safety, null key/value support, unlimited key count, and value-based numeric equality that Apache lacks
- PERFORMANCE:
ClassValueSet- Optimized with Class identity checkscontains()andremove()now useo.getClass() == Class.classinstead ofinstanceofclear()no longer creates unnecessary HashSet copy before invalidating cache
- NEW:
IdentitySet<T>- High-performance generic Set using object identity (==) instead ofequals()- Lightweight replacement for
Collections.newSetFromMap(new IdentityHashMap<>()) - Extends
AbstractSet<T>and implements fullSet<T>interface includingiterator() - Uses open addressing with linear probing for excellent cache locality
- Single
Object[]array - no Entry objects, no Boolean values - ~8 bytes per element vs ~40-48 bytes for IdentityHashMap-backed Set
- Generic type safety:
IdentitySet<Object>,IdentitySet<Class<?>>,IdentitySet<Map<?,?>>, etc. - Ideal for cycle detection, visited tracking, and identity-based membership tests
- Lightweight replacement for
- PERFORMANCE: Replaced
Collections.newSetFromMap(new IdentityHashMap<>())withIdentitySet<T>in:Traverser- object graph traversal visited tracking (IdentitySet<Object>)MapUtilities- map structure cycle detection (IdentitySet<Map<?,?>>)ObjectConversions- object-to-map conversion visited tracking (IdentitySet<Object>)DeepEquals- deep hash code and format value cycle detection (IdentitySet<Object>)ClassUtilities- inheritance chain traversal visited tracking (IdentitySet<Class<?>>)
- PERFORMANCE: Replaced
HashSet<Class<?>>withIdentitySet<Class<?>>for faster identity-based lookups:ReflectionUtils- Class hierarchy traversal infindClassAnnotation(),getMethodAnnotation(), interface graph BFS, and method lookup (4 locations)DeepEquals- Custom equals class ignore setCaseInsensitiveMap- Registry duplicate Class checkClassValueSet-retainAll()removal tracking and iterator snapshotConverter- Type variations tracking
4.81.0 - 2025-01-10
- PERFORMANCE:
StringUtilities.equals(CharSequence, CharSequence)- Optimized for CharSequence-to-String comparisons- Now uses
String.contentEquals(CharSequence)when either argument is a String String.contentEquals()is JVM-intrinsic optimized and handles StringBuilder efficiently (direct char array comparison)- Previous implementation only optimized String-to-String comparisons, missing the common case of StringBuilder-to-String
- Now uses
- ARCHITECTURE:
CompactMap- Replaced runtime Java compilation with pre-compiled bytecode template- No longer requires JDK: The builder API (
CompactMap.builder().build()) now works on JRE, not just JDK - How it works: A pre-compiled bytecode template is patched at runtime with the configuration hash, then static fields are injected with configuration values (case sensitivity, compact size, ordering, etc.)
- Performance: Eliminates JavaCompiler overhead - template classes are created via
MethodHandles.Lookup.defineClass()with simple byte array patching - Same functionality: All existing builder options work exactly as before (case sensitivity, ordering, compact size, custom map types, etc.)
- Backward compatible: Subclassing
CompactMapcontinues to work unchanged - Disabled obsolete
CompactMapMethodsTest.testGetJavaFileForOutputAndOpenOutputStream(tested old JavaFileManager approach)
- No longer requires JDK: The builder API (
- BUG FIX:
Converter- Fixed char[]/byte[] cross-conversion returning null afterisConversionSupportedFor()was called- Root cause:
VoidConversions::toNullwas used as a placeholder inCONVERSION_DBto "advertise" that char[] ↔ byte[] conversions are supported - When
isConversionSupportedFor(char[].class, byte[].class)was called, this placeholder got cached - Subsequent
convert(char[], byte[].class)found the cached placeholder and used it, returning null instead of performing actual conversion - Fix: Replaced placeholder entries with actual converters that call
ArrayConversions.arrayToArray() - Added tests:
testIsConversionSupportedForDoesNotBreakConvert,testArrayCrossConversionWithAndWithoutSupportCheck
- Root cause:
- BUG FIX:
AbstractConcurrentNullSafeMap.computeIfAbsent()- Fixed contention causing hangs under concurrent load- Affects:
ConcurrentHashMapNullSafeandConcurrentNavigableMapNullSafe(both extend AbstractConcurrentNullSafeMap) - Root cause: Used
ConcurrentHashMap.compute()which holds a lock on the hash bin for the entire duration of the mapping function execution. Under high concurrency, threads pile up waiting for bin locks, causing hangs. - Key insight: The common case (no null values stored) doesn't need special sentinel handling during computation
- Fix: Fast path now delegates directly to
ConcurrentHashMap.computeIfAbsent()- this is lock-free for cache hits and only briefly locks for insertions. The slow path (key mapped to null, which is rare) uses optimistic locking withputIfAbsent()/replace(). - Result: Performance is now virtually identical to plain
ConcurrentHashMapfor typical use cases (caching, memoization, etc.)
- Affects:
- TEST FIX:
LRUCacheTest- Added proper cleanup to prevent OutOfMemoryError during deployment tests- The
testSpeedandtestCacheBlasttests create 10M-entry caches (~800MB each) - Added
@AfterEachcleanup that clears and nullifies caches, then suggests GC - Added explicit
cache.clear()at the end of large tests to free memory before next test
- The
- IMPROVED: Scheduler lifecycle management for
ThreadedLRUCacheStrategyandTTLCache- Both classes now have proper shutdown methods that await termination (up to 5 seconds graceful, then 1 second forced)
- Schedulers are now lazily recreatable - if shut down, creating new cache instances automatically restarts them
ThreadedLRUCacheStrategy.shutdownScheduler()- new static method for explicit cleanupTTLCache.shutdown()- now returns boolean indicating clean termination, properly awaits termination- All schedulers use daemon threads, so they won't prevent JVM shutdown even without explicit cleanup
- IMPROVED:
ThreadedLRUCacheStrategy- Complete algorithm redesign with zone-based eviction and sample-15 approximate LRU- Memory guarantee: Cache never exceeds 2x capacity, allowing predictable worst-case memory sizing
- Four-zone eviction strategy:
- Zone A (0 to 1x capacity): Normal operation, no eviction
- Zone B (1x to 1.5x capacity): Background cleanup only (every 500ms)
- Zone C (1.5x to 2x capacity): Probabilistic inline eviction (0% at 1.5x → 100% at 2x)
- Zone D (2x+ capacity): Hard cap with evict-before-insert
- Sample-15 eviction: Instead of sorting all entries O(n log n), samples 15 entries and evicts the oldest. Based on Redis research, this provides ~99% LRU accuracy with O(1) cost per eviction.
- No more sorting: Eliminated all O(n log n) sort operations - entire algorithm is now O(1) for inline operations
- Concurrent race fix: Zone D eviction now uses a while-loop to guarantee hard cap enforcement. Previously, multiple threads could all check
size < hardCap, then all insert, causing unbounded growth (check-then-act race condition). Now inserts first, then loops eviction until under hard cap. - Fixes deployment hang: Heavy write load (500k unique keys into 5k cache) no longer causes hangs due to expensive sorting or unbounded cache growth
4.80.0 - 2025-01-05
- PERFORMANCE: Cache repeated
getClass()calls in hot pathsConcurrentNavigableMapNullSafe- Cacheo1.getClass()ando2.getClass()in comparator (6 calls → 2)CollectionUtilities- Cache class lookups in deep copy operations for source, pair.source, and element objectsGraphComparator- CachesrcValue.getClass()andtargetValue.getClass()in compare loop (7 calls → 2)
- PERFORMANCE:
ClassUtilitiesandGraphComparator- ReplaceisAssignableFrom(obj.getClass())withisInstance(obj)ClassUtilities.java:2278- Constructor parameter type matching now usesisInstance()GraphComparator.java:375- Field type validation now usesisInstance()isInstance()avoids thegetClass()call, providing better performance on hot paths
- FIX:
ClassUtilities- Fixed unsafe mode reentrancy issue with counter-based ThreadLocal approach- Previously, nested
setUseUnsafe(true)/setUseUnsafe(false)calls would incorrectly disable unsafe mode for outer callers - Changed from
ThreadLocal<Boolean>toThreadLocal<Integer>counter (unsafeDepth) setUseUnsafe(true)increments the counter,setUseUnsafe(false)decrements (but not below 0)- Unsafe mode is active when counter > 0, supporting proper nesting
- Added comprehensive tests for nested calls, triple nesting, extra disables, and thread isolation
- Previously, nested
- FIX:
ThreadedLRUCacheStrategy- Major concurrency fixes and architectural improvements:- Architecture: Replaced per-instance ScheduledFutures with single shared cleanup thread for all cache instances. Uses WeakReference registry allowing unused caches to be garbage collected. Dead references pruned automatically during iteration.
- Fix silent task death: Wrapped cleanup in try-catch to prevent ScheduledExecutorService from silently cancelling the task on exceptions.
- Fix sort instability: Capture timestamps to array before sorting to prevent
IllegalArgumentException: Comparison method violates its general contractcaused by concurrent timestamp updates during sort. - Aggressive cleanup: When cache exceeds 10x capacity, removes all excess items immediately instead of batching.
- PERFORMANCE:
AbstractConcurrentNullSafeMap.computeIfAbsent()- Added fast-path check before locking- Original implementation always called
compute()which locks the bucket even for cache hits - New implementation checks
get()first - cache hits now bypass locking entirely - Significant reduction in lock contention under concurrent load
- Original implementation always called
- ADDED:
computeIfAbsent()andputIfAbsent()support to LRU cache strategiesThreadedLRUCacheStrategy- atomic operations with timestamp updatesLockingLRUCacheStrategy- atomic operations with LRU reorderingLRUCache- delegation to underlying strategy
4.72.0 - 2025-12-31
- BUG FIX: Fixed Jackson dependencies incorrectly declared without
<scope>test</scope>. Jackson (jackson-databind, jackson-dataformat-xml) is only used for testing and should not be a transitive dependency. This restores java-util's zero external runtime dependencies. - BUG FIX: Fixed
ThreadedLRUCacheStrategyscheduled task accumulation. WhenCaseInsensitiveMap.replaceCache()was called multiple times, each new cache scheduled a purge task that was never cancelled. These orphaned tasks accumulated and could overwhelm the scheduler thread. Now:ThreadedLRUCacheStrategy.shutdown()properly cancels the scheduled purge taskLRUCache.shutdown()delegates to the strategy's shutdownCaseInsensitiveMap.replaceCache()calls shutdown on the old cache before replacing
- UPDATED: Test dependencies updated to latest versions:
- jackson-databind: 2.17.2 → 2.20.1
- jackson-dataformat-xml: 2.17.2 → 2.20.1
4.71.0 - 2025-12-31
- PERFORMANCE:
MultiKeyMap.expandAndHash()- Multiple optimizations for faster key processing:- Fast-path for common leaf types: Added early-exit check for common final types (String, Integer, Long, Double, Boolean, Short, Byte, Character, Float) using class identity comparison (
==) instead of falling through theinstanceofchain. Since these are the most frequent key types, checking them first skips the more expensiveinstanceofchecks (Map, Set, Collection) for 80%+ of calls. - Array detection optimization: Replaced
isArray()withgetComponentType() != nullfor slight additional speedup. - Set allocation elimination: Eliminated per-element ArrayList allocation when processing Sets. Previously created N temporary ArrayLists and N
addAll()calls for a Set with N elements. Now adds elements directly to result list. - Benchmark impact: 3 additional wins vs Apache Commons MultiKeyMap (29 wins vs 26 before), converting ties to wins with no new losses.
- Fast-path for common leaf types: Added early-exit check for common final types (String, Integer, Long, Double, Boolean, Short, Byte, Character, Float) using class identity comparison (
- PERFORMANCE:
MultiKeyMap.keysMatch()- Removed redundant conditional check where both branches performed identical operations. - PERFORMANCE:
Converter.getInheritedConverter()- Added caching for inheritance pairs:- cacheCompleteHierarchy: Caches full type hierarchy including level 0
- cacheInheritancePairs: Uses MultiKeyMap to cache sorted pairs per (source, target)
- InheritancePair class: Holds cached pair data without instanceId
- First call for each (source, target) pair builds and caches the sorted pairs. Subsequent calls use O(1) cache lookup + iteration over pre-sorted list.
- IMPROVED:
Converter.getSupportedConversions()andallSupportedConversions()- Now correctly report all dynamic container conversions:- Array/Collection conversions: Object[] ↔ Collection, array to array
- Enum conversions: Array/Collection/Map to Enum (creates EnumSet), EnumSet to Collection/Object[]
isConversionSupportedFor(): Added optimistic handling for Object[] source component type - returns true when source component is Object.class (can't know actual element types at compile time)
- CLEANUP:
FastReader- Removed unused extended API methods that were never utilized by json-io:- Deprecated:
getLine()andgetCol()now return 0 (line/column tracking removed for performance) - Removed line/column tracking overhead from hot path
getLastSnippet()provides error context without per-character tracking cost
- Deprecated:
4.70.0 - 2025-11-18
- ADDED:
RegexUtilities- New utility class providing thread-safe pattern caching and ReDoS (Regular Expression Denial of Service) protection for Java regex operations. This class addresses two critical concerns in regex-heavy applications:
- Pattern Caching: Thread-safe ConcurrentHashMap-based caching of compiled Pattern objects to eliminate redundant Pattern.compile() overhead. Supports three caching strategies:
- Standard patterns:
getCachedPattern(String)- Most common case- Case-insensitive patterns:
getCachedPatternCaseInsensitive(String)- Dedicated cache for CASE_INSENSITIVE flag- Custom flags:
getCachedPattern(String, int)- Supports any combination of Pattern flags (MULTILINE, DOTALL, etc.)- ReDoS Protection: Timeout-based regex execution prevents catastrophic backtracking attacks that cause CPU exhaustion. All safe* methods use configurable timeouts (default 5000ms) with shared ExecutorService for high-performance operation:
safeMatches(Pattern, String)- Timeout-protected pattern matchingsafeFind(Pattern, String)- Returns SafeMatchResult with captured groupssafeReplaceFirst(Pattern, String, String)- Safe replacement operationssafeReplaceAll(Pattern, String, String)- Safe global replacementsafeSplit(Pattern, String)- Safe string splitting- Invalid Pattern Handling: Returns null for malformed patterns instead of throwing exceptions, with patterns cached to avoid repeated compilation attempts
- Performance: Shared CachedThreadPool ExecutorService eliminates per-operation thread creation overhead (previously caused 74% performance degradation, now zero overhead)
- Configuration: System property controls via cedarsoftware.security.enabled, cedarsoftware.regex.timeout.enabled, cedarsoftware.regex.timeout.milliseconds
- Observability:
getPatternCacheStats()provides cache metrics (total patterns, invalid patterns, cache sizes per strategy)- Thread Safety: All operations are thread-safe with daemon threads to prevent JVM shutdown blocking
- Test Coverage: Comprehensive test suite with 17,807 tests passing, including pattern caching verification, timeout protection, and invalid pattern handling
- Use Cases: Prevents regex-based DoS attacks, improves performance for frequently-used patterns, provides unified regex API across Cedar Software projects (java-util, json-io, n-cube)
- PERFORMANCE:
FastReader- Multiple performance and reliability optimizations:
- Made class
final: Prevents subclassing and enables JVM optimizations (method inlining, devirtualization)- Inlined
movePosition()in hot path: Eliminated ~1.5M method calls per MB of JSON by inlining line/column tracking directly inread()andread(char[], int, int)methods- Improved EOF handling: Added early-exit check (
if (limit == -1) return;) infill()to avoid redundant read attempts after EOF- Optimized
read(char[], int, int): Reduced local variable allocations and improved loop efficiency by hoisting position/offset updates outside inner loops- Fixed pushback tracking: Corrected line/column position reversal when characters are pushed back (changed
0x0ato'\n'for clarity)- Pre-sized StringBuilder:
getLastSnippet()now pre-allocates capacity to avoid internal array resizing- Increased default buffer sizes: Changed from 10 to 16 pushback buffers (60% more capacity) for better handling of complex tokenization scenarios
- Impact: These micro-optimizations compound in json-io's parsing hot path where
FastReadermethods are called millions of times per large JSON file- IMPROVED:
StringConversions.toPattern()- Updated to useRegexUtilities.getCachedPattern()for pattern caching and ReDoS protection. The Converter framework's String → Pattern conversion now benefits from:
- Pattern Caching: Eliminates redundant Pattern.compile() calls when same pattern string is converted multiple times
- ReDoS Protection: Timeout-protected compilation prevents malicious regex patterns from causing CPU exhaustion
- Invalid Pattern Handling: Returns clear IllegalArgumentException for invalid patterns instead of propagating PatternSyntaxException
- Backward Compatibility: All existing Pattern conversion tests pass (5 tests in PatternConversionsTest)
- Performance: Same pattern string returns cached instance on subsequent conversions
- PERFORMANCE:
DataGeneratorInputStream.withRandomBytes()- ~7x faster via two synergistic optimizations (profiler-verified):
- Optimization 1 - Unbounded nextInt(): Use
random.nextInt() & 0xFFinstead ofrandom.nextInt(256). Profiler measurements: 4,410ms → 2,517ms (1.75x speedup, 43% faster). Unbounded nextInt() avoids internal bound-checking overhead in Random class.- Optimization 2 - Byte extraction batching: Extract 4 bytes from each Random call using bit shifting instead of 1 byte per call. Reduces Random method calls by 75% (4x reduction). Expected additional 4x speedup: 2,517ms → ~630ms.
- Combined impact: ~7x total speedup (4,410ms → ~630ms) by multiplying both optimizations (1.75x × 4x ≈ 7x)
- Implementation: Uses bit masking (
buffer & 0xFF) and unsigned right shift (buffer >>>= 8) to extract 4 bytes sequentially from 32-bit int. Maintains internal buffer withbytesRemainingcounter.- Branch elimination: Creates two separate IntSupplier implementations (one for includeZero=true, one for false) instead of checking flag on every
getAsInt()call.- Random instance lifecycle: Created once per stream (not in factory method), following same pattern as withRepeatingPattern() and withSequentialBytes()
- Deterministic: Maintains exact same random sequences for same seeds - all DataGeneratorInputStream tests pass with identical output
- PERFORMANCE:
MultiKeyMapstripe lock count increased for better concurrency under high load:
- Changed formula:
cores * 2instead ofcores / 2(4x more stripes on typical systems)- Increased max: 128 stripes (was 32) to support high-core-count servers (64+ cores)
- 12-core example: 8 stripes → 32 stripes (4x more parallelism)
- Impact: Profiler showed severe lock contention in
putInternal()- 6,588ms blocked waiting for locks. With 4x more stripes, contention drops dramatically as threads distribute across more locks.- Rationale: Matches ConcurrentHashMap's DEFAULT_CONCURRENCY_LEVEL approach (concurrency = cores or higher) for optimal write parallelism
- Backward compatible: Auto-tuned based on CPU cores, no API changes
- PERFORMANCE:
ConcurrentList.size()andisEmpty()made O(1) lock-free using dedicated size counter:
- Problem: Profiler flame graph showed
size()as prominent hot spot. Previous implementation usedreadLockaroundtail.get() - head.get(), causing lock acquisition overhead on frequently-called methods.- Solution: Added dedicated
AtomicLong sizeCountermaintained by all add/remove operations.size()andisEmpty()now just read the counter - no locks, no calculations.- Previous:
readLock.lock()→tail - head→readLock.unlock()(lock overhead + two volatile reads)- New:
sizeCounter.get()(single volatile read, no locks)- Correctness: Initially attempted to remove readLock from
tail - headcalculation, but Claude Sonnet 4.5 correctly identified thread-safety issue (inconsistent snapshots could cause negative sizes). Dedicated counter solves this properly.- Overhead: Adds one
incrementAndGet()/decrementAndGet()to add/remove operations, but these already have locks or atomic operations, so impact is minimal compared to massive speedup of lock-freesize().- Impact: Eliminates readLock acquisition for size queries (called 100+ times across codebase), reduces lock contention, improves scalability
- All ConcurrentList tests pass (including concurrency stress tests)
- TEST PERFORMANCE:
ConcurrentList2Test- Multiple optimizations eliminating 88+ seconds and reducing random number overhead:
- Problem 1: Found two lines computing
int start = random.nextInt(random.nextInt(list.size()))- nested calls taking 44+ seconds each, butstartvariable was unused (gray in IDE).- Fix 1: Removed unused random number generation from iterator/listIterator test threads.
- Problem 2: Using
SecureRandominstead ofRandom(SecureRandom is much slower, unnecessary for tests).- Fix 2: Changed to regular
Random- tests don't need cryptographic randomness.- Problem 3: Using
random.nextInt(bound)which profiler showed is 1.75x slower than unboundednextInt().- Fix 3: Changed to unbounded
nextInt()with bit masking/modulo:random.nextInt() % 3,(random.nextInt() & 0x3FF) + 1000, etc.- Problem 4: Unused
subListRunnabledefined but never executed (dead code).- Fix 4: Removed unused runnable and associated imports.
- Impact: Eliminates 88+ seconds of wasted CPU time (44.27s + 44.22s), plus significant reduction in modifier thread overhead from faster random generation. Same test coverage, much faster execution.
- IMPROVED:
IOUtilitiestransfer methods now return byte counts - Alltransfer*()methods now return the number of bytes transferred instead of void, enabling callers to verify transfer completion and track progress:
- Methods returning
long:transfer(InputStream, OutputStream),transfer(InputStream, OutputStream, callback),transfer(File, OutputStream),transfer(InputStream, File, callback),transfer(URLConnection, File, callback),transfer(File, URLConnection, callback)- Uselongto support files and streams larger than 2GB (Integer.MAX_VALUE)- Methods returning
int:transfer(InputStream, byte[]),transfer(URLConnection, byte[])- Useintsince Java arrays are bounded by Integer.MAX_VALUE- Backward compatible: Existing code that ignores return values continues to work unchanged
- Test coverage: Added 6 comprehensive tests using
DataGeneratorInputStreamto verify byte counts for transfers ranging from 0 bytes to 5MB, including callback verification and pattern validation- Use cases: Enables verification of complete transfers, progress tracking for large files, detecting truncated transfers, and audit logging of transfer operations
- FIXED:
DeepEquals- Fixed 7 critical and high-severity thread safety and reliability issues found via comprehensive adversarial code review:
- Deep recursion StackOverflowError - Recursive deepEquals calls now inherit remaining depth budget instead of resetting to full budget, preventing unbounded recursion when custom equals() methods trigger nested comparisons. Added budget inheritance logic that passes (maxDepth - currentDepth) to recursive calls.
- Hash/equals contract violation - Fixed hashDouble() and hashFloat() to maintain contract that equal values have equal hashes. Changed quantization from 1e12 to 1e10 (100× coarser) to match epsilon-based equality tolerance (1e-12), preventing HashMap/HashSet storage failures. Trade-off: ~40% hash collisions for values 2-10× epsilon apart (acceptable - narrow band).
- ThreadLocal memory leak - Added try-finally blocks with ThreadLocal.remove() in deepHashCode() entry point to prevent memory leaks in long-running applications, especially those using thread pools where threads are reused.
- Unbounded memory allocation - Moved depth budget tracking from options Map to separate ThreadLocal stack, making options Map stable and reusable. Reduced HashMap allocations from 500,000 to ~2 for 1M-node graphs (500,000× improvement), preventing OutOfMemoryError with large object graphs.
- SimpleDateFormat race condition - Replaced
ThreadLocal<SimpleDateFormat>with SafeSimpleDateFormat for date formatting in diff output. SafeSimpleDateFormat provides copy-on-write semantics and per-thread LRU cache, preventing corrupted date formatting from re-entrant callbacks.- formattingStack re-entrancy - Changed formattingStack from
ThreadLocal<Set<Object>>toThreadLocal<Deque<Set<Object>>>(stack of Sets), where each top-level formatValue() call gets its own Set for circular reference detection. Prevents false<circular Object>detection when re-entrant deepEquals calls format the same object in different contexts.- Unsafe visited set publication - Replaced HashSet with ConcurrentSet for visited set tracking. ConcurrentSet uses weakly consistent iterators (backed by ConcurrentHashMap) that never throw ConcurrentModificationException, providing fail-safe behavior instead of fail-fast when inputs are modified concurrently.
- Test coverage: Added 37 comprehensive tests across 6 new test classes verifying all fixes. All 17,726 existing tests pass with zero regressions.
- Performance: Minimal overhead for normal usage, with massive improvements for edge cases (500,000× fewer allocations for large graphs, 100 MB → 400 bytes memory usage)
- Backward compatibility: 100% backward compatible - all public APIs unchanged, behavior identical for normal usage patterns
4.3.0 - 2025-11-07
- ADDED:
DataGeneratorInputStream- A flexible, memory-efficientInputStreamthat generates data on-the-fly using various strategies without consuming memory to store the data. This class is ideal for testing code that handles large streams, generating synthetic test data, or creating pattern-based input. Supports multiple generation modes:
- Random bytes: Generates random bytes (0-255) with optional seed for reproducible tests. Can exclude zero bytes if needed for specific testing scenarios
- Repeating patterns: Repeats a string or byte array pattern cyclically (e.g., "ABC" → ABCABCABC...)
- Constant byte: Outputs the same byte value repeatedly for simple fill patterns
- Sequential bytes: Counts sequentially between two byte values with automatic wrap-around. Supports both ascending (10→20) and descending (20→10) sequences
- Random strings: Generates random proper-case alphabetic strings (like "Xkqmz Pqwer Fgthn") using
StringUtilities.getRandomString(), separated by configurable delimiters- Custom generator: Accepts any
IntSupplierlambda for complete flexibility- All methods use static factory pattern (
withRandomBytes(),withRepeatingPattern(), etc.) for clear, readable code- Zero memory overhead - data is generated on-demand and immediately discarded, enabling efficient testing with TB+ scale streams
- Thread-safe read operations with proper
InputStreamcontract compliance- Full JavaDoc with comprehensive examples for each generation mode
4.2.0 - 2025-11-02
FIXED:
MultiKeyMapnested Set lookup bug in COLLECTIONS_EXPANDED mode - Fixed size mismatch false negatives when looking up keys containing expanded Collections. In COLLECTIONS_EXPANDED mode, stored keys have expanded size (includes SET_OPEN/SET_CLOSE markers) while lookup keys have un-expanded Collection size. Added skipSizeCheck logic to bypass size comparison for Collection-to-Collection matches in expanded mode, allowing compareCollections() to handle the structural comparison correctly. This fixes lookups failing incorrectly when using nested Sets or Collections as multi-keys.IMPROVED: Code quality improvements from comprehensive IntelliJ IDEA inspection analysis (17 fixes across 5 classes):
- MultiKeyMap: Improved comment precision (arity → size), enhanced Javadoc clarity, optimized variable declarations for better readability
- StringUtilities: Enhanced null safety with explicit checks, improved loop variable scoping, added type casting safety guards, optimized string concatenation patterns
- ConcurrentList: Improved synchronization block granularity, enhanced iterator safety, optimized size calculations with better caching
- ClassUtilities: Reduced cognitive complexity in findClosest(), improved exception handling clarity, enhanced method parameter validation
- CaseInsensitiveMap: Optimized keySet() and values() operations, improved type safety in internal operations, enhanced edge case handling
- All changes maintain 100% backward compatibility while improving code maintainability and reducing potential edge case issues
FIXED: Map and Set hashCode() contract compliance - Removed incorrect
EncryptionUtilities.finalizeHash()calls from 6 classes that violated the Map and Set interface contracts. The Map contract requireshashCode() = sum of entry hashCodes, and the Set contract requireshashCode() = sum of element hashCodes. Using finalizeHash() broke the Object.hashCode() contract (equal objects must have equal hashCodes) and caused HashSet/HashMap storage failures. Fixed classes:AbstractConcurrentNullSafeMap,TTLCache,LockingLRUCacheStrategy,ThreadedLRUCacheStrategy,ConcurrentSet,ClassValueSet.CHANGED:
IOUtilitiesclose/flush methods now throw exceptions as unchecked - Breaking behavioral change: Allclose()andflush()methods inIOUtilities(forCloseable,Flushable,XMLStreamReader,XMLStreamWriter) now throw exceptions as unchecked viaExceptionUtilities.uncheckedThrow()instead of silently swallowing them. This change provides:
- Better diagnostics: Close/flush failures are now visible rather than silently hidden
- Cleaner code: No try-catch required at call sites - works seamlessly in finally blocks
- Early problem detection: Infrastructure issues (disk full, network failures, resource exhaustion) surface immediately
- Caller flexibility: Exceptions can still be caught higher in the call stack if desired
- Important: While close/flush exceptions are rare, when they occur they often indicate serious issues that should be diagnosed rather than hidden. This change makes java-util consistent with its existing philosophy of throwing checked exceptions as unchecked (see
transfer(),compressBytes(), etc. which already use this pattern).ADDED: Geometric primitives in dedicated
geompackage - The 5 AWT-replacement classes (Point,Rectangle,Dimension,Insets,Color) are organized incom.cedarsoftware.util.geomfollowing Java's package organization pattern (java.awt.geom). This provides:
- Clear organization: Geometric/graphical primitives grouped separately from general utilities
- Enhanced documentation: All classes prominently state "Zero-dependency - No java.desktop/java.awt required" with emphasis on headless server/microservices use
- Full module support: Package exported via both JPMS module descriptor and OSGi MANIFEST
FIXED: Added missing
cachepackage to JPMS and OSGi exports - Thecom.cedarsoftware.util.cachepackage (containingLockingLRUCacheStrategyandThreadedLRUCacheStrategy) was not exported in module descriptors. Addedexports com.cedarsoftware.util.cache;to moditect configuration and OSGi Export-Package directive. This ensures the cache package is properly accessible to both JPMS modules and OSGi bundles.IMPROVED: Added comprehensive cloud-native and containerization documentation to README - Added prominent "Cloud Native & Container Ready" section highlighting java-util's advantages for modern cloud deployments:
- Platform badges: AWS, Azure, GCP, Kubernetes, Docker compatibility
- Container optimization: Minimal footprint (~1.1MB total, 85% smaller than Guava), zero dependencies, fast startup optimized for serverless/FaaS
- Deployment guide: Platform-specific advantages for AWS Lambda/ECS/EKS, Azure Functions/AKS, GCP Cloud Run/GKE, Kubernetes, Docker
- Performance examples: Dockerfile showing 50% image size reduction, Kubernetes YAML demonstrating lower resource requests
- Serverless ready: Explicit callouts for Lambda, Cloud Functions, Cloudflare Workers, edge computing
- Enterprise security: Minimal attack surface, no Log4Shell exposure, SOC 2/FedRAMP/PCI-DSS compliance benefits
REMOVED: java.awt/java.desktop dependency eliminated - Created 5 Cedar DTO classes (
Color,Dimension,Point,Rectangle,Insets) to replace java.awt equivalents, completely removing the java.desktop module dependency. This enables:
- Headless deployment: No display system required - ideal for servers, containers, and cloud platforms
- Smaller footprint: Eliminates 100MB+ java.desktop module from runtime
- Cloud-ready: Compatible with AWS Lambda, GraalVM native-image, Docker distroless images
- Faster startup: 2-3x improvement without loading AWT/Swing infrastructure
- Reduced attack surface: Removes entire GUI subsystem from security considerations
- AWT-compatible API: Cedar DTOs use identical method signatures (getRed(), getWidth(), etc.) for seamless migration
- Backward-compatible parsing: StringConversions accepts both "Dimension[...]" and "java.awt.Dimension[...]" formats for existing serialized data
- Java 8 compatible: Uses final classes with private fields (not Records), maintaining Java 8 baseline while enabling future Record migration (Java 17+)
PERFORMANCE: Zero-allocation multi-key lookups with ThreadLocal arrays - Added explicit overloads for
getMultiKey(k1, k2)throughgetMultiKey(k1..k5)andcontainsMultiKey(k1, k2)throughcontainsMultiKey(k1..k5)that use ThreadLocal<Object[]> arrays (one per size: LOOKUP_KEY_2 through LOOKUP_KEY_5). Eliminates varargs array allocation on every multi-key lookup call. For lookup-only operations, the ThreadLocal arrays are reused per thread and only used for comparison (never stored), providing zero-allocation lookups for the most common 2-5 key cases. Expected to improve MultiKeyMap performance vs Apache Commons MultiKeyMap in benchmark scenarios.PERFORMANCE: Simplified
Convertercache lookups using MultiKeyMap's ThreadLocal optimization - RefactoredgetCachedConverter()to useFULL_CONVERSION_CACHE.getMultiKey(source, target, instanceId)directly, eliminating Converter's own ThreadLocal<Object[3]>. Leverages MultiKeyMap's internal LOOKUP_KEY_3 ThreadLocal for zero-allocation lookups. Cleaner code (removed redundant ThreadLocal, simplified getCachedConverter from 8 lines to 4) with identical performance - MultiKeyMap's getMultiKey() provides the same ~26% speedup over varargs.FIXED:
MultiKeyMapcollection key handling in COLLECTIONS_NOT_EXPANDED mode - Fixed two critical issues: (1) keysMatch() now uses collection.equals() instead of element-by-element comparison for proper equality semantics across different Collection implementations (e.g., Arrays.ArrayList vs Collections.UnmodifiableRandomAccessList), (2) entrySet() now preserves original collection types instead of reconstructing them, preventing hash code mismatches after deserialization. These fixes ensure collection keys can be looked up correctly after serialization/deserialization cycles.REMOVED:
MultiKeyMap.entries(),MultiKeyEntry, andEntryIterator- Removed deprecatedentries()method that exposed internal flattened key structure with markers. All code now uses standardentrySet()which returns keys as native List/Set/single structures suitable for serialization. This cleanup:
- Eliminates 3 unused public methods:
externalizeNulls(),externalizeMarkers(),internalizeMarkers()- Removes 2 classes: public
MultiKeyEntryand privateEntryIterator- Refactors internal methods (
keySet(),values(),hashCode(),toString()) to useentrySet()or direct bucket iteration- Migrates all tests and
Converterto useentrySet()instead of deprecatedentries()REMOVED:
MultiKeyMapreconstruct method consolidation - Consolidated multiple reconstruct methods into a single cleanreconstructKey()method:
- Removed
reconstructKeyForSerialization()redundant wrapper method- Removed deprecated
reconstructKey()that returned Object[] arrays- Removed
collectElements(),keyView(),externalizeAndWrapKey(), andexternalizeKey()helper methods- Renamed
reconstructKeyToNative()→reconstructKey()as the single public key reconstruction method- Updated json-io's
MultiKeyMapFactoryto remove old format handling withinternalizeMarkers()FIXED:
MultiKeyMapcritical NULL_SENTINEL equality bug - FixedelementEquals()to properly check equality after normalizing NULL_SENTINEL to null. Previously, NULL_SENTINEL and null were incorrectly treated as unequal, breaking null key handling.FIXED:
ConcurrentListpollFirst()/pollLast() lock usage - Changed pollFirst() and pollLast() to use write locks instead of read locks. Previously they used read locks while modifying data (setting bucket elements to null), creating a race condition with toArray() which also uses read locks. This caused toArray() to see "phantom nulls" from concurrent removals. With write locks, poll operations are properly synchronized with read operations, allowing toArray() to safely preserve legitimate null values.IMPROVED:
MultiKeyMapperformance optimizations:
- Removed duplicate getClass() calls: Eliminated redundant
getClass()andisArray()calls inflattenKey()by declaring variables once and reusing across code paths- Optimized instanceof checks: Deferred rare atomic array type checks until after confirming key is an array, eliminating 9 redundant instanceof checks across 3 hot-path methods (normalizeForLookup, findSimpleOrComplexKey, createMultiKey)
- Increased Set comparison threshold: Changed from 3 to 6 elements for nested O(n²) comparison vs HashMap allocation. Benchmarking shows 36 comparisons (6²) is faster than HashMap overhead, improving Set/List performance ratio from 6.15x to 4.8x (22% improvement)
IMPROVED:
MultiKeyMapcapacity and size handling:
- Switched to AtomicLong for size tracking: Migrated from AtomicInteger to AtomicLong to support maps with billions of entries (beyond 2³¹-1 limitation) with zero performance impact
- Added longSize() method: Returns true size as
longwithout Integer.MAX_VALUE cap, enabling accurate size reporting for very large maps- Enhanced size() documentation: Documents Integer.MAX_VALUE capping behavior and directs users to longSize() for maps exceeding 2³¹-1 entries
IMPROVED:
MultiKeyMapdocumentation enhancements:
- Fixed misleading volatile read comments: Corrected 5 instances where comments incorrectly stated
table.length()was a volatile read (array lengths are immutable). Comments now accurately describe that only thebucketsreference read is volatile- Documented Map contract violations: Added explicit documentation that
entrySet(),keySet(), andvalues()return snapshots (not live views), explaining the rationale and performance trade-offs of snapshot semantics in concurrent contexts- Added Big-O complexity documentation: New class-level section documenting performance characteristics (O(k) for get/put/remove, O(1) for size, O(n) for snapshots) with clear explanations of complexity variables
- Added capacity/size limits documentation: Comprehensive section explaining AtomicLong usage, Integer.MAX_VALUE capping in size(), memory requirements (~200-300GB for 2³¹ entries), and feasibility on modern servers
- Enhanced Set key examples: Added detailed examples showing Sets combined with Object[] and List keys, demonstrating order-agnostic Set matching vs order-dependent List matching in multi-dimensional keys
- Removed commented-out code: Eliminated 6 commented AWT/Swing array type references to reduce code clutter and avoid JPMS/OSGi headless deployment confusion
IMPROVED:
MultiKeyMapperformance optimizations forequals()andhashCode():
- Optimized equals() implementation: Refactored to walk the OTHER map and query THIS map using
get(), eliminating unnecessary key reconstruction on our side. Reduces work by 50% and eliminates all extra memory allocations during equality checks- Added hashCode() caching: Implemented cached hashCode with invalidation on mutations (put, remove, clear). First call computes O(n*k), subsequent calls are O(1). Provides massive speedup for maps used in HashSets or as keys in other maps
IMPROVED:
MultiKeyMaptoString() now uses distinct notation for Lists vs Sets:
- Lists use square brackets
[1, 2, 3](order-sensitive)- Sets use curly braces
{4, 5, 6}(order-agnostic)- Mixed keys clearly show both:
🆔 [1, 2, 3], {4, 5, 6} → 🟣 value- Nested structures properly display with appropriate delimiters
ADDED:
MultiKeyMapjson-io serialization support -MultiKeyMapinstances can now be serialized and deserialized using json-io, preserving complex multi-dimensional keys including Lists, Sets, Arrays, and null values. Keys are serialized in their native structures (not flattened arrays), making the JSON output clean and human-readable. This enablesMultiKeyMapuse in distributed caching, persistent storage, REST APIs, and microservices communicationADDED: Comprehensive test coverage for MultiKeyMap equals(), hashCode(), and toString() functionality:
MultiKeyMapEqualsHashCodeTest: 25 tests verifying equals/hashCode contracts with mixed List/Set keysMultiKeyMapToStringTest: 21 tests verifying correct List/Set notation in outputMultiKeyMapMixedListSetTest: 16 tests verifying order-sensitive List and order-agnostic Set matching
4.1.0
FIXED:
ClassUtilities.setUseUnsafe()is now thread-local instead of global, preventing race conditions in multi-threaded environments where concurrent threads need different unsafe mode settingsIMPROVED:
ClassUtilitiescomprehensive improvements from GPT-5 review:🔒 SECURITY FIXES:
- Enhanced class loading security with additional blocked prefixes: Added blocking for
jdk.nashorn.package to prevent Nashorn JavaScript engine exploitation; added blocking forjava.lang.invoke.MethodHandles$Lookupclass which can open modules reflectively and bypass security boundaries- Added percent-encoded path traversal blocking: Enhanced resource path validation to block percent-encoded traversal sequences (%2e%2e, %2E%2E, etc.) before normalization; prevents bypass attempts using URL encoding
- Enhanced resource path security: Added blocking of absolute Windows drive paths (e.g., "C:/...", "D:/...") in resource loading to prevent potential security issues
- Enhanced security blocking: Added package-level blocking for
javax.script.*to prevent loading of any class in that package- Added belt-and-suspenders alias security: addPermanentClassAlias() now validates classes through SecurityChecker.verifyClass() to prevent aliasing to blocked classes
- Fixed security bypass in cache hits: Alias and cache hits now properly go through SecurityChecker.verifyClass() to prevent bypassing security checks
- Updated Unsafe permission check: Replaced outdated "accessClassInPackage.sun.misc" permission with custom "com.cedarsoftware.util.enableUnsafe" permission appropriate for modern JDKs
- Simplified resource path validation: Removed over-eager validation that blocked legitimate resources, focusing on actual security risks (.., null bytes, backslashes)
- Improved validateResourcePath() precision: Made validation more precise - now only blocks null bytes, backslashes, and ".." path segments (not substrings), allowing legitimate filenames like "my..proto"
⚡ PERFORMANCE OPTIMIZATIONS:
- Optimized constructor matching performance: Eliminated redundant toArray() calls per constructor attempt by converting collection to array once
- Optimized resource path validation: Replaced regex pattern matching with simple character checks, eliminating regex engine overhead
- Optimized findClosest() performance: Pull distance map once from ClassHierarchyInfo to avoid repeated computeInheritanceDistance() calls
- Optimized findLowestCommonSupertypesExcluding performance: Now iterates the smaller set when finding intersection
- Optimized findInheritanceMatches hot path: Pre-cache ClassHierarchyInfo lookups for unique value classes
- Optimized loadClass() string operations: Refactored JVM descriptor parsing to count brackets once upfront, reducing string churn
- Optimized hot-path logging performance: Added isLoggable() guards to all varargs logging calls to prevent unnecessary array allocations
- Optimized getParameters() calls: Cached constructor.getParameters() results to avoid repeated allocations
- Optimized buffer creation: Cached zero-length ByteBuffer and CharBuffer instances to avoid repeated allocations
- Optimized trySetAccessible caching: Fixed to actually use its accessibility cache, preventing repeated failed setAccessible() attempts
- Added accessibility caching: Implemented caching for trySetAccessible using synchronized WeakHashMap for memory-safe caching
- Prevented zombie cache entries: Implemented NamedWeakRef with ReferenceQueue to automatically clean up dead WeakReference entries
🐛 BUG FIXES:
- Fixed interface depth calculation: Changed ClassHierarchyInfo to use max BFS distance instead of superclass chain walking
- Fixed tie-breaking for common supertypes: Changed findLowestCommonSupertypesExcluding to sort by sum of distances from both classes
- Fixed JPMS SecurityException handling: Added proper exception handling for trySetAccessible calls under JPMS
- Fixed nameToClass initialization inconsistency: Added "void" type to static initializer and included common aliases in clearCaches()
- Fixed tie-breaker logic: Corrected shouldPreferNewCandidate() to properly prefer more specific types
- Fixed areAllConstructorsPrivate() for implicit constructors: Method now correctly returns false for classes with no declared constructors
- Fixed mutable buffer sharing: ByteBuffer, CharBuffer, and array default instances are now created fresh on each call
- Fixed inner class construction: Inner class constructors with additional parameters beyond enclosing instance are now properly matched
- Fixed varargs ArrayStoreException vulnerability: Added proper guards when packing values into varargs arrays
- Fixed named-parameter gating: Constructor parameter name detection now checks ALL parameters have real names
- Fixed Currency default creation: Currency.getInstance(Locale.getDefault()) now gracefully falls back to USD
- Fixed generated-key Map ordering: Fixed bug where Maps with generated keys could inject nulls when keys had gaps
- Fixed loadResourceAsBytes() leading slash handling: Added fallback to strip leading slash when ClassLoader.getResourceAsStream() fails
- Fixed OSGi class loading consistency: OSGi framework classes now loaded using consistent classloader
- Fixed ClassLoader key mismatch: Consistently resolve null ClassLoader to same instance
- Fixed computeIfAbsent synchronization: Replaced non-synchronized computeIfAbsent with properly synchronized getLoaderCache()
- Fixed off-by-one in class load depth: Now validates nextDepth instead of currentDepth
- Fixed OSGi/JPMS classloader resolution: Simplified loadClass() to consistently use getClassLoader() method
- Fixed permanent alias preservation: Split aliases into built-in and user maps so clearCaches() preserves user-added permanent aliases
- Fixed removePermanentClassAlias loader cache invalidation: Both add and remove methods now properly clear per-loader cache entries
- Fixed findLowestCommonSupertypesExcluding NPE: Added null-check for excluded parameter
- Fixed ArrayStoreException in matchArgumentsWithVarargs: Added final try-catch guard for exotic conversion edge cases
- Fixed OSGi loader cache cleanup: clearCaches() now properly clears the osgiClassLoaders cache
- Fixed OSGi cache NPE: Fixed potential NullPointerException in getOSGiClassLoader() when using computeIfAbsent()
- Fixed incorrect comment: Updated accessibilityCache comment to correctly state it uses Collections.synchronizedMap
🎯 API IMPROVEMENTS:
- Added boxing support in computeInheritanceDistance(): Primitive types can now reach reference types through boxing
- Added primitive widening support: Implemented JLS 5.1.2 primitive widening conversions (byte→short→int→long→float→double)
- Added Java-style array support: loadClass() now supports Java-style array names like "int[][]" and "java.lang.String[]"
- Added varargs constructor support: Implemented proper handling for varargs constructors
- Enhanced varargs support with named parameters: newInstanceWithNamedParameters() now properly handles varargs parameters
- Improved API clarity for wrapper types: Changed getArgForType to only provide default values for actual primitives
- Improved API clarity: Renamed defaultClass parameter to defaultValue in findClosest() method
- Fixed API/docs consistency for null handling: All primitive/wrapper conversion methods now consistently throw IllegalArgumentException
- Added null safety: Made doesOneWrapTheOther() null-safe, returning false for null inputs
- Added cache management: Added clearCaches() method for testing and hot-reload scenarios
- Added deterministic Map fallback ordering: When constructor parameter matching falls back to Map.values() and Map is HashMap, values are sorted alphabetically
- Implemented ClassLoader-scoped caching: Added WeakHashMap-based caching with ClassLoader keys and WeakReference values
📚 DOCUMENTATION & CLEANUP:
- Updated documentation: Enhanced class-level Javadoc and userguide.md to accurately reflect all public methods
- Documented Map ordering requirement: Added documentation to newInstance() methods clarifying LinkedHashMap usage
- Improved documentation clarity: Updated computeInheritanceDistance() documentation to clarify caching
- Added comprehensive edge case test coverage: Created ClassUtilitiesEdgeCaseTest with tests for deep interface hierarchies
- Added tests for public utility methods: Added tests for logMethodAccessIssue(), logConstructorAccessIssue(), and clearCaches()
- Removed deprecated method: Removed deprecated indexOfSmallestValue() method
- Removed unused private method: Removed getMaxReflectionOperations() and associated constant
- Removed unnecessary flush() call: Eliminated no-op ByteArrayOutputStream.flush() in readInputStreamFully()
- Clarified Converter usage: Added comment explaining why ClassUtilities uses legacy Converter.getInstance()
🔧 CONFIGURATION & DEFAULTS:
- Fixed surprising default values: Changed default instance creation to use predictable, stable values:
- Date/time types now default to epoch (1970-01-01) instead of current time
- UUID defaults to nil UUID (all zeros) instead of random UUID
- Pattern defaults to empty pattern instead of match-all ".*"
- URL/URI mappings commented out to return null instead of potentially connectable localhost URLs
- Removed problematic defaults:
- Removed EnumMap default mapping to TimeUnit.class
- Removed EnumSet.class null supplier from ASSIGNABLE_CLASS_MAPPING
- Removed Class.class → String.class mapping
- Removed Comparable→empty string mapping
- Preserved mapping order: Changed ASSIGNABLE_CLASS_MAPPING to LinkedHashMap for deterministic iteration
- Improved immutability: Made PRIMITIVE_WIDENING_DISTANCES and all inner maps unmodifiable
- Reduced logging noise: Changed various warnings from WARNING to FINE level for expected JPMS violations
- Improved OSGi loader discovery order: Changed getClassLoader() to try context loader first, then anchor, then OSGi
- Improved resource path handling for Windows developers: Backslashes in resource paths are now normalized to forward slashes
- Simplified primitive checks: Removed redundant isPrimitive() OR checks since methods handle both primitives and wrappers
- Simplified SecurityManager checks: Removed redundant ReflectPermission check in trySetAccessible()
- Made record support fields volatile: Proper thread-safe lazy initialization for JDK 14+ features
IMPROVED:
CaseInsensitiveSetrefactored to useCollections.newSetFromMap()for cleaner implementation:
- Simplified implementation using Collections.newSetFromMap(CaseInsensitiveMap) internally
- Added Java 8+ support: spliterator(), removeIf(Predicate), and enhanced forEach() methods
- Fixed removeAll behavior for proper case-insensitive removal with non-CaseInsensitive collections
- Maintained full API compatibility
FIXED:
DeepEqualscollection comparison was too strict when comparing different Collection implementations:
- Fixed UnmodifiableCollection comparison with Lists/ArrayLists based on content
- Relaxed plain Collection vs List comparison as unordered collections
- Preserved Set vs List distinction due to incompatible equality semantics
FIXED:
SafeSimpleDateFormatthread-safety and lenient mode issues:
- Fixed NPE in setters by initializing parent DateFormat fields
- Fixed lenient propagation to both Calendar and SimpleDateFormat
- Keep parent fields in sync when setters are called
IMPROVED:
SafeSimpleDateFormatcompletely redesigned with copy-on-write semantics:
- Copy-on-write mutations create new immutable state snapshots
- Thread-local LRU caching for SimpleDateFormat instances
- No locks on hot path - format/parse use thread-local cached instances
- Immutable state tracking for all configuration
- Smart cache invalidation on configuration changes
- Backward compatibility maintained
FIXED:
UniqueIdGeneratorJava 8 compatibility:
- Fixed Thread.onSpinWait() using reflection for Java 9+, no-op fallback for Java 8
PERFORMANCE: Optimized
DeepEqualsbased on GPT-5 code review:
- Algorithm & Data Structure Improvements:
- Migrated from LinkedList to ArrayDeque for stack operations
- Pop-immediately optimization eliminating double iterations
- Depth tracking optimization avoiding costly parent chain traversal
- Early termination optimization using LIFO comparison order
- Primitive array optimization comparing directly without stack allocations
- Pre-size hash buckets to avoid rehashing on large inputs
- Fixed O(n²) path building using forward build and single reverse
- Optimized probe comparisons to bypass diff generation completely
- Added Arrays.equals fast-path for primitive arrays
- Optimized decomposeMap to compute hash once per iteration
- Added fast path for integral number comparison avoiding BigDecimal
- Correctness Fixes:
- Changed epsilon value from 1e-15 to 1e-12 for practical floating-point comparisons
- Adjusted hash scales to maintain hash-equals contract with new epsilon
- Fixed List comparison semantics - Lists only compare equal to other Lists
- Fixed floating-point comparison using absolute tolerance for near-zero
- Made NaN comparison consistent via bitwise equality
- Fixed hash-equals contract for floating-point with proper NaN/infinity handling
- Fixed infinity comparison preventing infinities from comparing equal to finite numbers
- Fixed ConcurrentModificationException using iterator.remove()
- Fixed formatDifference crash using detailNode approach
- Fixed deepHashCode bucket misalignment with slow-path fallback
- Fixed leftover detection for unmatched elements
- Fixed visited set leakage in candidate matching
- Fixed non-monotonic depth budget clamping
- Fixed deepHashCode Map collisions using XOR for key-value pairs
- Features & Improvements:
- Added Java Record support using record components instead of fields
- Added Deque support with List compatibility
- Improved sensitive data detection with refined patterns
- Improved MAP_MISSING_KEY error messages with clearer formatting
- Added security check in formatComplexObject for sensitive fields
- Added string sanitization for secure errors
- Type-safe visited set using
Set<ItemsToCompare>- Skip static/transient fields in formatting
- Implemented global depth budget across recursive paths
- Added Locale.ROOT for consistent formatting
- Gated diff_item storage behind option to prevent retention
- Added DIFF_ITEM constant for type-safe usage
- Code Quality:
- Removed static initializer mutating global system properties
- Removed unreachable AtomicInteger/AtomicLong branches
- Fixed Javadoc typos and added regex pattern commentary
- Fixed documentation to match default security settings
- Performance micro-optimizations hoisting repeated lookups
SECURITY & CORRECTNESS:
ReflectionUtilscomprehensive fixes based on GPT-5 security audit:
- Fixed over-eager setAccessible() only for non-public members
- Fixed getNonOverloadedMethod enforcement for ANY parameter count
- Added interface hierarchy search using breadth-first traversal
- Fixed method annotation search traversing super-interfaces
- Fixed trusted-caller bypass - ReflectionUtils no longer excludes itself
- Removed static System.setProperty calls during initialization
- Fixed Javadoc typo: Corrected "instants hashCode()" to "instance's hashCode()" in deepHashCode documentation
- Added regex pattern commentary: Clarified that HEX_32_PLUS and UUID_PATTERN use lowercase patterns since strings are lowercased before matching
- Type-safe visited set: Changed visited set type from
Set<Object>toSet<ItemsToCompare>for compile-time type safety and to prevent accidental misuse- Added Arrays.equals fast-path: Use native Arrays.equals for primitive arrays as optimization before element-by-element comparison with diff tracking
- Skip static/transient fields in formatting: Aligned formatComplexObject and formatValueConcise with equality semantics by skipping static and transient fields
- Implemented global depth budget: Pass remaining depth budget through child calls to ensure security limits are truly global across all recursive paths, preventing excessive recursion
- Additional nuanced fixes from GPT-5 review:
- Fixed non-monotonic depth budget: Clamp child budget to tighter of inherited budget and remaining configured budget to prevent depth limit bypass
- Added string sanitization for secure errors: Sanitize map keys and string values when secure errors are enabled to prevent sensitive data leakage
- Optimized decomposeMap: Avoid rehashing keys multiple times by computing hash once per iteration
- Fixed deepHashCode Map collisions: Hash key-value pairs together using XOR for order-independent hashing that reduces collisions
- Added Locale.ROOT for numeric formatting: Ensure consistent decimal formatting across all locales
- Added Deque support with List compatibility: List and Deque now compare as equal when containing the same ordered elements, treating both as ordered sequences that allow duplicates (berries over branches philosophy)
- Fixed visited set leakage in candidate matching: Use copies of visited set for exploratory candidate comparisons in unordered collections and maps to prevent pollution with failed comparison state
- Fixed documentation to match default security settings: Updated Javadoc to correctly state that default safeguards are enabled (100k limits for collections/arrays/maps, 1k for object fields, 1M for recursion depth)
- Added fast path for integral number comparison: Avoid expensive BigDecimal conversion for Byte, Short, Integer, Long, AtomicInteger, and AtomicLong comparisons
- Added special case handling for AtomicInteger and AtomicLong: Use get() methods directly like AtomicBoolean, avoiding reflective field access for better performance and consistency
- Precompiled sensitive data regex patterns: Avoid regex compilation overhead on every call to looksLikeSensitiveData() by using precompiled Pattern objects
- Added Enum handling as simple type: Use reference equality (==) for enum comparisons and format as EnumType.NAME, avoiding unnecessary reflective field walking
IMPROVED:
ReflectionUtilsenhancements based on GPT-5 review:
- Fixed getMethod interface search: Now properly searches entire interface hierarchy using BFS traversal to find default methods
- Removed pre-emptive SecurityManager checks: Removed unnecessary SecurityManager checks from call() methods since setAccessible is already wrapped
- Documented null-caching requirement: Added clear documentation to all cache setter methods that custom Map implementations must support null values
- Fixed getClassAnnotation javadoc: Corrected @throws documentation to accurately reflect that only annoClass=null throws, classToCheck=null returns null
4.0.0
- FEATURE: Added
deepCopyContainers()method toCollectionUtilitiesandArrayUtilities:
- Deep Container Copy: Iteratively copies all arrays and collections to any depth while preserving references to non-container objects ("berries")
- Iterative Implementation: Uses heap-based traversal with work queue to avoid stack overflow on deeply nested structures
- Circular Reference Support: Properly handles circular references, maintaining the circular structure in the copy
- Enhanced Type Preservation:
- EnumSet → EnumSet (preserves enum type)
- Deque → LinkedList (preserves deque operations, supports nulls)
- PriorityQueue → PriorityQueue (preserves comparator and heap semantics)
- SortedSet → TreeSet (preserves comparator and sorting)
- Set → LinkedHashSet (preserves insertion order)
- List → ArrayList (optimized for random access)
- Other Queue types → LinkedList (preserves queue operations)
- Performance Optimizations:
- Primitive arrays use
System.arraycopyfor direct copying without boxing/unboxing overhead- Primitive arrays at root level are not queued (already fully copied)
- Collections are pre-sized to avoid resize/rehash operations during population
- Only containers are queued for processing, eliminating per-element allocations
- Direct array access for object arrays instead of reflection in tight loops
- Pre-sized IdentityHashMap (64) to avoid rehash thrashing
- EnumSet uses efficient
clone().clear()for empty sets- Maps as Berries: Maps are treated as non-containers and not deep copied
- Thread Safety Note: Method is not thread-safe under concurrent source mutation
- FEATURE: Added
caseSensitiveconfiguration option toMultiKeyMap:
- Case-Sensitive Mode: New constructor
MultiKeyMap(boolean caseSensitive)allows case-sensitive String key comparisons (default remains case-insensitive)- Performance Optimization: Eliminated per-key branching by storing caseSensitive as final field, improving JIT optimization
- Full API Support: Case sensitivity applies to all MultiKeyMap operations including standard Map interface and multi-key methods
- Documentation: Updated README.md with examples showing case-sensitive vs case-insensitive behavior
- DOCUMENTATION: Updated README.md to document MultiKeyMap's advanced configuration options:
- Added examples for case-sensitive mode configuration
- Added examples for value-based equality mode for cross-type numeric comparisons
- Updated comparison table showing MultiKeyMap's unique features vs competitors
- MAJOR PERFORMANCE OPTIMIZATION: Enhanced
MultiKeyMapwith comprehensive performance improvements based on GPT5 code review:
- Fixed KIND_COLLECTION Fast Path: Added
!valueBasedEqualitycheck to gate fast path, ensuring collections with numerically equivalent but type-different elements match correctly (e.g., [1,2,3] matches [1L,2L,3L] in value-based mode)- Optimized compareNumericValues: Replaced with highly optimized version using same-class fast paths, avoiding BigDecimal conversion for common cases. Added helper methods:
isIntegralLike,isBig,extractLongFast,toBigDecimal- Defensive RandomAccess Checking: Added
instanceof Listchecks beforeinstanceof RandomAccessin 6 locations to prevent ClassCastException- Branch-Free Loop Optimization: Split loops by
valueBasedEqualitymode in 7 comparison methods, eliminating per-element branching for better JIT optimization and CPU branch prediction- Avoided Primitive Boxing: Refactored
comparePrimitiveArrayToObjectArrayto avoid boxing in type-strict mode with direct type checking- Collapsed Duplicate Type Ladders: Created
primVsListandprimVsIterhelper methods, eliminating redundant 8-type switch statements and reducing bytecode size- Consolidated Symmetric Methods: Made symmetric comparison methods delegate to their counterparts, reducing code duplication
- NaN Handling for Primitive Arrays: Added special NaN handling for double[] and float[] arrays respecting valueBasedEquality mode
- Ref-Equality Guards: Added
if (a == b) continue;guards in all comparison loops, leveraging JVM caching for common values- PERFORMANCE ENHANCEMENT: Enhanced
MultiKeyMapwith significant hash computation optimizations:
- Hash Computation Limit: Added MAX_HASH_ELEMENTS (4) limit to bound hash computation for large arrays/collections, significantly improving performance
- Early Exit Optimization: Hash computation now stops early for large containers while maintaining excellent hash distribution
- Dimensionality Check Optimization: Separated hash computation from dimensionality detection for better performance on large containers
- ArrayList Optimization: Added specialized fast path for ArrayList iteration avoiding iterator overhead
- Primitive Array Optimizations: Enhanced hash computation for String[], int[], long[], double[], and boolean[] arrays with bounded processing
- Generic Array Processing: Improved reflection-based array processing with hash computation limits
- Collection Processing: Optimized both ArrayList and generic Collection processing with early termination
- Performance Testing: Added comprehensive test coverage including hash distribution analysis, collision analysis, and performance comparisons
3.9.0
- MAJOR FEATURE: Enhanced
MultiKeyMapwith comprehensive performance and robustness improvements:
- Security Enhancement: Replaced String sentinels with custom objects to prevent key collisions in internal operations
- Performance Optimization: Added comprehensive collection and typed array optimizations with NULL_SENTINEL uniformity
- Performance: Enhanced MultiKeyMap visual formatting and optimized ArrayList iteration patterns
- Hash Algorithm: Added MurmurHash3 finalization for improved hash distribution
- Bug Fix: Fixed instanceof Object[] hierarchy issues ensuring proper type handling across all array types
- Enhancement: Improved null key handling and enhanced toString() formatting with proper emoji symbols
- Simplification: Streamlined MultiKeyMap implementation for better maintainability and performance
- Test Coverage: Added comprehensive test coverage including:
- Generic array processing test coverage ensuring robust type handling
- MultiKeyMap.formatSimpleKey method testing for output consistency
- NULL_SENTINEL and cycle detection test coverage for edge case robustness
- Fixed MultiKeyMapMapInterfaceTest emoji format expectations
- ENHANCEMENT:
IntervalSetimprovements:
- Simplified Architecture: Uses half-open intervals [start, end) eliminating need for custom boundary functions
- API Enhancement: Mirrors ConcurrentSkipListSet's behavior more accurately
- New Feature: Added snapshot() method for obtaining point-in-time snapshots with better return types than toArray()
- JSON Round-Trip Support: Added constructor that accepts snapshot() output, enabling easy JSON serialization/deserialization round-trips
- Bug Fix: Fixed JSON serialization constructors for proper deserialization support
- Documentation: Added comprehensive quanta calculation examples using Math.nextUp() and temporal precision APIs
- DOCUMENTATION: Updated changelog.md and improved table formatting throughout documentation
3.8.0
- MAJOR FEATURE: Added
IntervalSet- thread-safe set of half-open intervals [start, end). Optimized (collapsed) by default, or all intervals retained ifautoMerge=false(audit mode):
- Half-Open Semantics: Uses [start, end) intervals where start is inclusive, end is exclusive - eliminates boundary ambiguity
- High Performance: O(log n) operations using
ConcurrentSkipListMapfor all queries, insertions, and range operations- Dual Storage Modes: Auto-merge mode (default) merges overlapping intervals; discrete mode preserves all intervals for audit trails
- Rich Query API: Navigation methods (
nextInterval,previousInterval,higherInterval,lowerInterval), containment checking, and range queries- Simplified Boundaries: Half-open intervals eliminate need for complex boundary calculations while supporting all Comparable types
- Thread Safety: Lock-free reads with minimal write locking; weakly consistent iteration reflects live changes; use
snapshot().iterator()for point-in-time iteration- Quanta Support: Comprehensive documentation for creating minimal intervals using Math.nextUp(), temporal precision, and integer arithmetic
- Type Support: Full support for Integer, Long, Date, Timestamp, LocalDate, ZonedDateTime, Duration, and all Comparable types
- Comprehensive Testing: 116+ test cases covering all data types, concurrent operations, edge cases, and both storage modes
- TEST FIX: Stabilized
ConcurrentListIteratorTest.testReadFailsGracefullyWhenConcurrentRemoveShrinksListby using a latch to reliably detect the expected exception under heavy load- BUG FIX: Prevented null elements from appearing in iterator snapshots of
ConcurrentListunder extreme concurrency- BUG FIX: Corrected
IntervalSetrange removal operations, enforced unique start keys in discrete mode, and improved type support documentation.- REFACTOR: Simplified
MultiKeyMapby removing the redundant volatilesizefield and relying on the existingAtomicIntegerfor size tracking.- REFACTOR: Consolidated hash computation logic in
MultiKeyMapto reduce duplication and improve readability.
3.7.0
- MAJOR FEATURE: Enhanced
MultiKeyMapwith N-dimensional array expansion support:
- N-Dimensional Array Expansion: Nested arrays of any depth are automatically flattened recursively into multi-keys with sentinel preservation
- Visual Notation:
{{"a", "b"}, {"c", "d"}} → [SENTINELS, DN, "a", "b", UP, DN, "c", "d", UP]- powerful structural preservation- Iterative Processing: Uses stack-based approach to avoid recursion limits with deeply nested arrays
- Universal Support: Works with jagged arrays, mixed types, null elements, and empty sub-arrays
- API Consistency: Full support across all MultiKeyMap APIs (put/get/containsKey/remove and putMultiKey/getMultiKey/removeMultiKey/containsMultiKey)
- Comprehensive Testing: 13 test cases covering 2D/3D arrays, mixed types, jagged arrays, deep nesting, and edge cases
- SECURITY ENHANCEMENT: Enhanced
TrackingMapwith SHA-1 based key tracking to eliminate array component ambiguity:
- Ambiguity Resolution: Different array structures
[[a,b],[c,d]]vs[a,b,c,d]now track distinctly using SHA-1 hashes- Structural Sentinels: Added
LEVEL_DOWN/LEVEL_UP/HAS_SENTINELSobjects to preserve array nesting information- Hash-Based Tracking: Multi-dimensional arrays tracked via SHA-1 hash of expanded sentinel structure
- Performance Optimized: O(1) sentinel detection using
HAS_SENTINELSflag, shorter string representations for speed- Clean APIs:
MultiKeyMap.get1DKey()andcomputeSHA1Hash()provide focused functionality for TrackingMap- API ENHANCEMENT: Updated
MultiKeyMapvarargs method names for disambiguation:
- Renamed Methods:
put()→putMultiKey(),get()→getMultiKey(),remove()→removeMultiKey(),containsKey()→containsMultiKey()- Backward Compatibility: Standard Map interface methods (single key) remain unchanged
- Documentation Updated: README.md, userguide.md, and Javadoc all reflect correct API usage
- ENUM SIMPLIFICATION: Streamlined
MultiKeyMap.CollectionKeyModefrom 3 to 2 values:
- Simplified Options:
COLLECTIONS_EXPANDED(default) andCOLLECTIONS_NOT_EXPANDED- Clear Behavior: Arrays are ALWAYS expanded regardless of setting; enum only affects Collections
- Constructor Support: Enhanced constructors to accept
CollectionKeyModeparameter for configuration- Documentation Clarity: Updated all documentation to reflect simplified enum behavior
- BUG FIX: Fixed
ConcurrentListConcurrencyTest.testConcurrentQueueOperationstiming issue:
- Flaky Test Resolution: Updated test expectations to accommodate realistic concurrent producer/consumer timing variations
- Race Condition: Test was expecting perfect 100% consumption rate in concurrent scenario, but timing variations meant some
pollFirst()calls returned null- Improved Validation: Now validates ≥90% consumption rate and empty queue state, which properly tests ConcurrentList functionality
- No Functional Changes: This was a test-only fix; ConcurrentList behavior remains unchanged and correct
- PROCESS IMPROVEMENT: Enhanced deployment pipeline with updated Maven Sonatype publishing process
- PERFORMANCE: Optimized test execution by disabling compilation for faster test cycles during development
- TEST FIX: Stabilized
ConcurrentListIteratorTest.testReadFailsGracefullyWhenConcurrentRemoveShrinksList
- Used a latch to reliably detect the expected exception under heavy load
3.6.0
- MAJOR FEATURE: Added many additional types to
Converter, expanding conversion capability (1,700+ total conversion pairs):
- Atomic Arrays: Added full bidirectional conversion support for
AtomicIntegerArray,AtomicLongArray, andAtomicReferenceArray- NIO Buffers: Added complete bridge system for all NIO buffer types (
IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer,ShortBuffer) with existingByteBufferandCharBuffer- BitSet Integration: Added intelligent
BitSetconversion support with bridges toboolean[](bit values),int[](set bit indices), andbyte[](raw representation)- Stream API: Added bidirectional conversion support for
IntStream,LongStream, andDoubleStreamprimitive streams- Universal Array Access: Each array-like type now has access to the entire universal array conversion ecosystem - for example,
AtomicIntegerArray→int[]→Colorworks seamlessly- Performance Optimized: All bridges use efficient extraction/creation patterns with minimal overhead
- Removed redundant array surrogate pairs that were duplicating universal array system functionality
- MutliKeyMap - Yes, a MultiKeyMap that supports n-keys, creates no heap pressure for get() { no allocations (new) within get() execution path}, full thread-safety for all operations.
- ARCHITECTURE IMPROVEMENT: Enhanced
addConversion()method with comprehensive primitive/wrapper support:
- When adding a conversion involving primitive or wrapper types, the system now automatically creates ALL relevant combinations
- Example:
addConversion(UUID.class, Boolean.class, converter)now creates entries for both(UUID, Boolean)and(UUID, boolean)- Eliminates runtime double-lookup overhead in favor of storage-time enumeration for better performance
- Ensures seamless primitive/wrapper interoperability in user-defined conversions
- Code Simplification: Refactored implementation to leverage existing
ClassUtilitiesmethods, reducing complexity while maintaining identical functionality- API ENHANCEMENT: Added
ClassUtilities.toPrimitiveClass()method as complement to existingtoPrimitiveWrapperClass():
- Converts wrapper classes to their corresponding primitive classes (e.g.,
Integer.class→int.class)ConcurrentListnow uses chunked atomic buckets for lock-free deque operations. See userguide for architecture diagram and capabilities table
- Returns the same class if not a wrapper type, ensuring safe usage for any class
- Leverages optimized
ClassValueMapcaching for high-performance lookups- Centralizes primitive/wrapper conversion logic in
ClassUtilitiesfor consistency across java-util- BUG FIX: Fixed time conversion precision inconsistencies in
Converterfor consistent long conversion behavior:
- Consistency Fix: All time classes now consistently convert to/from
longusing millisecond precision (eliminates mixed millisecond/nanosecond behavior)- Universal Rule:
Duration→ long,Instant→ long,LocalTime→ long now all return milliseconds for predictable behavior- Round-trip Compatibility: Long ↔ time class conversions are now fully round-trip compatible with consistent precision
- BigInteger Unchanged: BigInteger conversions continue to use precision-based rules (legacy classes = millis, modern classes = nanos)
- Feature Options: Added configurable precision control for advanced use cases requiring nanosecond precision:
- System properties:
cedarsoftware.converter.modern.time.long.precision,cedarsoftware.converter.duration.long.precision,cedarsoftware.converter.localtime.long.precision- Per-instance options via
ConverterOptions.getCustomOption()- see Feature Options for Precision Control for details- Impact: Minimal - fixes inconsistent behavior and provides migration path through feature options
- Rationale: Eliminates confusion from mixed precision behavior and provides simple, memorable conversion rules
- Added
computeIfAbsentsupport toMultiKeyMapfor lazy value population- Added
putIfAbsentsupport toMultiKeyMapfor atomic insert when key is missing or mapped to null- Expanded
MultiKeyMapto fully implementConcurrentMap: addedcomputeIfPresent,compute,replace, andremove(key,value)- Fixed stripe locking in
MultiKeyMapto consistently useReentrantLock- Feature Enhancements:
- Supports conversion from String formats: hex colors (
#FF0000,FF0000), named colors (red,blue, etc.),rgb(r,g,b), andrgba(r,g,b,a)formats- Supports conversion from Map format using keys:
red,green,blue,alpha,rgb,color, andvalue- Supports conversion from Map format using short keys:
r,g,b, andafor compact representation- Supports conversion from int arrays:
[r,g,b]and[r,g,b,a]formats with validation- Supports conversion from numeric types: Integer/Long packed RGB/ARGB values
- Supports conversion to all above formats with proper round-trip compatibility
- Values are converted through
converter.convert()allowing String, AtomicInteger, Double, etc. as color component values- Added comprehensive test coverage with 38 test methods covering all conversion scenarios
- Eliminates need for custom Color factories in json-io and other serialization libraries
- The static
Converter.getInstance()method remains available for accessing the default shared instance- Security Enhancement: Fixed critical security vulnerabilities in
CompactMapdynamic code generation:
- Added strict input sanitization to prevent code injection attacks in class name generation
- Fixed memory leak by using
WeakReferencefor generated class caching to allow garbage collection- Fixed race condition in class generation by ensuring consistent OSGi/JPMS-aware ClassLoader usage
- Enhanced input validation in
Buildermethods with comprehensive null checks and range validation- Improved resource management during compilation with proper exception handling
- Security Enhancement: Fixed critical security issues in
ClassUtilities:
- Added strict security checks for unsafe instantiation with
RuntimePermissionvalidation- Enhanced reflection security in
trySetAccessible()to not suppressSecurityExceptions- Updated deprecated
SecurityManagerusage for Java 17+ compatibility with graceful fallback- Security Enhancement: Fixed critical security vulnerabilities in
ReflectionUtils:
- Added
ReflectPermissionsecurity checks to prevent unrestricted method invocation incall()methods- Created
secureSetAccessible()wrapper to prevent access control bypass attacks- Fixed cache poisoning vulnerabilities by using object identity (
System.identityHashCode) instead of string-based cache keys- Updated all cache key classes to use tamper-proof object identity comparison for security
- Enhanced security boundary enforcement across all reflection operations
- Security Enhancement: Fixed critical security vulnerabilities in
DateUtilities:
- Fixed Regular Expression Denial of Service (ReDoS) vulnerability by simplifying complex regex patterns
- Eliminated nested quantifiers and complex alternations that could cause catastrophic backtracking
- Fixed thread safety issue by making month names map immutable using
Collections.unmodifiableMap()- Added comprehensive input validation with bounds checking for all numeric parsing operations
- Enhanced error messages with specific field names and valid ranges for better debugging
- Security Enhancement: Fixed critical SSL certificate bypass vulnerability in
UrlUtilities:
- Added comprehensive security warnings to
NAIVE_TRUST_MANAGERandNAIVE_VERIFIERhighlighting the security risks- Deprecated dangerous SSL bypass methods with clear documentation of vulnerabilities and safer alternatives
- Fixed
getAcceptedIssuers()to return empty array instead of null for improved security- Added runtime logging when SSL certificate validation is disabled to warn of security risks
- Enhanced JUnit test coverage to verify security fixes and validate proper warning behavior
- Security Enhancement: Fixed ReDoS vulnerability in
DateUtilitiesregex patterns:
- Limited timezone pattern repetition to prevent catastrophic backtracking (max 50 characters)
- Limited nanosecond precision to 1-9 digits to prevent infinite repetition attacks
- Added comprehensive ReDoS protection tests to verify malicious inputs complete quickly
- Preserved all existing DateUtilities functionality (187/187 tests pass)
- Conservative fix maintains exact capture group structure for API compatibility
- Security Enhancement: Fixed thread safety vulnerability in
DateUtilitiestimezone mappings:
- Made
ABBREVIATION_TO_TIMEZONEmap immutable usingCollections.unmodifiableMap()- Used
ConcurrentHashMapduring initialization for thread-safe construction- Prevents external modification that could corrupt timezone resolution
- Eliminates potential race conditions in multi-threaded timezone lookups
- Added comprehensive thread safety tests to verify concurrent access protection
- Performance Optimization: Optimized
CollectionUtilitiesAPIs:
- Pre-size collections in
listOf()/setOf()to avoid resizing overhead- Replace
Collections.addAll()with direct loops for better performance- Use
Collections.emptySet/emptyListinstead of creating new instances- Updated codebase to use consistent collection APIs (
CollectionUtilities.setOf()vsArrays.asList())- Performance Optimization: Enhanced
CaseInsensitiveMapefficiency:
- Fixed thread safety issues in cache management with
AtomicReference- Optimized
retainAll()to avoid size() anti-pattern (O(1) vs potentially O(n))- Added
StringUtilities.containsIgnoreCase()method with optimizedregionMatchesperformance- Updated
CaseInsensitiveMapto use newcontainsIgnoreCaseinstead of doubletoLowerCase()- Performance Optimization: Enhanced
DateUtilitiesefficiency:
- Optimized timezone resolution to avoid unnecessary string object creation in hot path
- Only create uppercase strings for timezone lookups when needed, reducing memory allocation overhead
- Improved timezone abbreviation lookup performance by checking exact match first
- Security Enhancement: Fixed timezone handling security boundary issues in
DateUtilities:
- Added control character validation to prevent null bytes and control characters in timezone strings
- Enhanced exception information sanitization to prevent information disclosure
- Improved error handling with truncated error messages for security
- Preserved API compatibility by maintaining
ZoneRulesExceptionandDateTimeExceptionfor existing test expectations- Added case-insensitive GMT handling and additional validation of system-returned timezone IDs
- Code Quality: Enhanced
ArrayUtilitiesandByteUtilities:
- Fixed generic type safety in
EMPTY_CLASS_ARRAYusingClass<?>[0]- Added bounds validation to
ByteUtilities.isGzipped(offset)to preventArrayIndexOutOfBoundsException- Added time complexity documentation to
ArrayUtilities.removeItem()method (O(n))- Improved documentation for null handling and method contracts
- Performance Optimization: Replaced inefficient
String.matches()with pre-compiled regex patterns inClassUtilities- Updated a few more spots where internal reflection updated
ReflectionUtilscaching for better performance.- Performance Enhancement: Added concurrent performance optimizations to
CaseInsensitiveMap:
- Added
mappingCount()method for efficient concurrent map size queries- Added bulk parallel operations:
forEach(long, BiConsumer),forEachKey(long, Consumer),forEachValue(long, Consumer)- Added parallel search operations:
searchKeys(long, Function),searchValues(long, Function),searchEntries(long, Function)- Added parallel reduce operations:
reduceKeys(long, Function, BinaryOperator),reduceValues(long, Function, BinaryOperator),reduceEntries(long, Function, BinaryOperator)- Enhanced iterator implementations with concurrent-aware behavior for ConcurrentHashMap backing maps
- Optimized for ~95% native ConcurrentHashMap performance while maintaining case-insensitive functionality
- Added centralized thread-safe key unwrapping with comprehensive documentation
- Enhancement: Brought
CompactSetto parity withCompactMapfor concurrent functionality:
- Added
mapType()method toCompactSet.Builderfor specifying concurrent backing map types- Added support for
ConcurrentHashMapandConcurrentSkipListSetbacking collections- Enhanced builder pattern to support all concurrent collection types available in
CompactMap- Maintains automatic size-based transitions while respecting concurrent backing map selection
- Enhancement: Brought
CaseInsensitiveSetto parity withCaseInsensitiveMapconcurrent capabilities:
- Added
elementCount()method for efficient concurrent set size queries (delegates to backing map'smappingCount())- Added bulk parallel operations:
forEach(long, Consumer),searchElements(long, Function),reduceElements(long, Function, BinaryOperator)- Enhanced iterator implementation with concurrent-aware behavior inheriting from backing
CaseInsensitiveMap- Added
getBackingMap()method for direct access to underlyingCaseInsensitiveMapinstance- Full feature parity ensures consistent concurrent performance characteristics across case-insensitive collections
- Code Quality: Eliminated all unchecked cast warnings in concurrent null-safe map classes:
- Updated
AbstractConcurrentNullSafeMapmethod signatures to acceptObjectparameters instead of generic types- Updated
ConcurrentNavigableMapNullSafemethod signatures for type safety compliance- Improved overall type safety without breaking existing API compatibility
- Reduced compiler warnings from 15 to 0 across concurrent collection classes
- Documentation: Comprehensive README.md enhancements for professional project presentation:
- Added comprehensive badge section with Maven Central, Javadoc, license, and compatibility information
- Enhanced Quick Start section with practical code examples for common use cases
- Added Performance Benchmarks section showcasing speed improvements and memory efficiency
- Created comprehensive Feature Matrix table comparing java-util collections with JDK alternatives
- Added Security Features showcase highlighting 70+ security controls and defensive programming practices
- Enhanced Integration examples for Spring, Jakarta EE, Spring Boot, and microservices architectures
- Extracted Framework Integration Examples to separate
frameworks.mdfile with corrected cache constructor examples- Testing: Added comprehensive test coverage for all new concurrent functionality:
- 27 new JUnit tests for
CaseInsensitiveMapconcurrent operations covering thread safety and performance- 15 new JUnit tests for
CompactSetconcurrent functionality and builder pattern enhancements- 23 new JUnit tests for
CaseInsensitiveSetconcurrent operations and feature parity validation- Added multi-dimensional array conversion test matching README.md example for better documentation accuracy
3.5.0
Converter.getInstance()exposes the default instance used by the static APIClassUtilities.newInstance()acceptsMaparguments using parameter names and falls back to the no‑arg constructorConverter.convert()returns the source when assignment compatible (when no other conversion path is selected)- Throwable creation from a
Maphandles aliases and nested causes- Jar file is built with
-parametersflag going forward (increased the jar size by about 10K)
3.4.0
MapUtilities.getUnderlyingMap()now uses identity comparison to avoid false cycle detection with wrapper mapsConcurrentNavigableMapNullSafe.pollFirstEntry()andpollLastEntry()now return correct values after removalUrlInvocationHandler(deprecated) was finally removed.ProxyFactory(deprecated) was finally removed.withReadLockVoid()now suppresses exceptions thrown by the providedRunnableSystemUtilities.createTempDirectory()now returns a canonical path so that temporary directories resolve symlinks on macOS and other platforms.- Updated inner-class JSON test to match removal of synthetic
this$fields.- Fixed
ExecutorAdditionalTestto compare canonical paths for cross-platform consistency- Fixed
Map.Entry.setValue()for entries fromConcurrentNavigableMapNullSafeandAbstractConcurrentNullSafeMapto update the backing map- Map.Entry views now fetch values from the backing map so
toString()andequals()reflect updates- Fixed test expectation for wrapComparator to place null keys last
Converternow offers single-argument overloads ofisSimpleTypeConversionSupportedandisConversionSupportedForthat cache self-type lookups- Fixed
TTLCache.purgeExpiredEntries()NPE when removing expired entriesUrlUtilitiesno longer deprecated; certificate validation defaults to on, provides streaming API and configurable timeouts- Logging instructions merged into
userguide.md; README section condensedExceptionUtilitiesadds privateuncheckedThrowfor rethrowing anyThrowableuncheckedIOUtilitiesand related APIs now throwIOExceptionunchecked
3.3.3 LLM inspired updates against the life-long "todo" list.
TTLCachenow recreates its background scheduler if used afterTTLCache.shutdown().SafeSimpleDateFormat.equals()now correctly handles otherSafeSimpleDateFormatinstances.- Manifest cleaned up by removing
Import-Packageentries forjava.sqlandjava.xml- All
System.outandSystem.errprints replaced withjava.util.logging.Loggerusage.- Documentation explains how to route
java.util.loggingoutput to SLF4J, Logback, or Log4j 2 in the user guideArrayUtilities- new APIsisNotEmpty,nullToEmpty, andlastIndexOf; improvedcreateArray,removeItem,addItem,indexOf,contains, andtoArrayClassUtilities- safer class loading fallback, improved inner class instantiation and updated JavadocsCollectionConversions.arrayToCollectionnow returns a type-safe collectionCompactMap.getConfig()returns the library default compact size for legacy subclasses.ConcurrentHashMapNullSafe- fixed race condition incomputeIfAbsentand added constructor to specify concurrency level.StringConversions.toSqlDatenow preserves the time zone from ISO date strings instead of using the JVM default.ConcurrentListis nowfinal, implementsSerializableandRandomAccess, and uses a fairReentrantReadWriteLockfor balanced thread scheduling.ConcurrentList.containsAll()no longer allocates an intermediateHashSet.listIterator(int)now returns a snapshot-based iterator instead of throwingUnsupportedOperationException.Converter- factory conversions map made immutable and legacy caching code removedDateUtilitiesusesBigDecimalfor fractional second conversion, preventing rounding errors with high precision inputEncryptionUtilitiesnow uses AES-GCM with random IV and PBKDF2-derived keys. Legacy cipher APIs are deprecated. Added SHA-384, SHA3-256, and SHA3-512 hashing support with improved input validation.- Documentation for
EncryptionUtilitiesupdated to list all supported SHA algorithms and note heap buffer usage.Executornow usesProcessBuilderwith a 60-second timeout and provides anExecutionResultAPIIOUtilitiesimproved: configurable timeouts,inputStreamToBytesthrowsIOExceptionwith size limit, offset bug fixed inuncompressBytesMathUtilitiesnow validates inputs for empty arrays and null lists, fixes documentation, and improves numeric parsing performanceReflectionUtilscache size is configurable via thereflection.utils.cache.sizesystem property, usesStringUtilities.decode()now returnsnullwhen invalid hexadecimal digits are encountered.StringUtilities.getRandomString()validates parameters and throws descriptive exceptions.StringUtilities.count()uses a reliable substring search algorithm.StringUtilities.hashCodeIgnoreCase()updates locale compatibility when the default locale changes.StringUtilities.commaSeparatedStringToSet()returns a mutable empty set usingLinkedHashSet.StringUtilitiesaddssnakeToCamel,camelToSnake,isNumeric,repeat,reverse,padLeft, andpadRighthelpers.- Constants
FOLDER_SEPARATORandEMPTYare now immutable (final).- Deprecated
StringUtilities.createUtf8String(byte[])removed; usecreateUTF8String(byte[])instead.SystemUtilitieslogs shutdown hook failures, handles missing network interfaces and returns immutable address listsTestUtil.fetchResource,MapUtilities.cloneMapOfSets, and core cache methods.TrackingMap-replaceContents()replaces the misleadingsetWrappedMap()API.keysUsed()now returns an unmodifiableSet<Object>andexpungeUnused()prunes stale keys.- Fixed tests for
TrackingMap.replaceContentsandsetWrappedMapto avoid tracking keys during verificationUnsafenow obtains the sun.misc.Unsafe instance from thetheUnsafefield instead of invoking its constructor, preventing JVM crashes during testsTraversersupports lazy field collection, improved null-safe class skipping, and better error loggingTraversernow ignores synthetic fields, preventing traversal into outer class referencesTraverserlogs inaccessible fields atLevel.FINESTinstead of printing to STDERRTypeUtilities.setTypeResolveCache()validates that the supplied cache is not null and innerTypeimplementations now implementequalsandhashCodeUniqueIdGeneratorusesjava.util.loggingand reduces CPU usage while waiting for the next millisecond- Explicitly set versions for
maven-resources-plugin,maven-install-plugin, andmaven-deploy-pluginto avoid Maven 4 compatibility warnings- Added Javadoc for several public APIs where it was missing. Should be 100% now.
- JUnits added for all public APIs that did not have them (no longer relying on json-io to "cover" them). Should be 100% now.
- Custom map types under
com.cedarsoftware.ioallowed forCompactMap
3.3.2 JDK 24+ Support
LRUCache-getCapacity()API added so you can query/determine capacity of anLRUCacheinstance after it has been created.SystemUtilities.currentJdkMajorVersion()added to provide JDK8 thru JDK24 compatible way to get the JDK/JRE major version.CompactMap- When using the builder pattern with the .build() API, it requires being run with a JDK - you will get a clear error if executed on a JRE. Using CompactMap (or static subclass of it like CompactCIHashMap or one of your own) does not have this requirement. The withConfig() and newMap() APIs also expect to execute on a JDK (dynamica compilation).CompactSet- Has the same requirements regarding JDK/JRE as CompactMap.- Updated tests to support JDK 24+
- EST, MST, HST mapped to fixed offsets (‑05:00, ‑07:00, ‑10:00) when the property sun.timezone.ids.oldmapping=true was set
- The old‑mapping switch was removed, and the short IDs are now links to region IDs: EST → America/Panama, MST → America/Phoenix, HST → Pacific/Honolulu
3.3.1 New Features and Improvements
CaseInsensitiveMap/Setcompute hashCodes slightly faster because of update toStringUtilities.hashCodeIgnoreCase().It takes advantage of ASCII for Locale's that use Latin characters.CaseInsensitiveStringinsideCaseInsensitiveMapimplementsCharSequenceand can be used outsideCaseInsensitiveMapas a case-insensitive but case-retentiative String and passed to methods that takeCharSequence.FastReader/FastWriter- tests added to bring it to 100% Class, Method, Line, and Branch coverage.FastByteArrayInputStream/FastByteArrayOutputStream- tests added to bring it to 100% Class, Method, Line, and Branch coverage.TrackingMap.setWrappedMap()- added to allow the user to set the wrapped map to a different map. This is useful for testing purposes.- Added tests for CompactCIHashSet, CompactCILinkedSet and CompactLinkedSet constructors.
3.3.0 New Features and Improvements
CompactCIHashSet, CompactCILinkedSet, CompactLinkedSet, CompactCIHashMap, CompactCILinkedMap, CompactLinkedMapare no longer deprecated. SubclassingCompactMaporCompactSetis a viable option if you need to serialize the derived class with libraries other thanjson-io,like Jackson, Gson, etc.- Added
CharBuffer to Map,ByteBuffer to Map,and vice-versa conversions.DEFAULT_FIELD_FILTERinReflectionUtilsmade public.- Bug fix:
FastWritermissing characters on buffer limit #115 by @ozhelezniak-talend.
3.2.0 New Features and Improvements
- Added
getConfig()andwithConfig()methods toCompactMapandCompactSet
- These methods allow easy inspection of
CompactMap/CompactSetconfigurations- Provides alternative API for creating a duplicate of a
CompactMap/CompactSetwith the same configuration- If you decide to use a non-JDK
Mapfor theMapinstance used byCompactMap, you are no longer required to have both a default constructor and a constructor that takes an initialize size.**- Deprecated
shutdownAPI onLRUCacheas it now uses a Daemon thread for the scheduler. This means that the thread will not prevent the JVM from exiting.
3.1.1
- ClassValueMap added. High-performance
Mapoptimized for ultra-fastClasskey lookups using JVM-optimizedClassValue- ClassValueSet added. High-performance
Setoptimized for ultra-fastClassmembership testing using JVM-optimizedClassValue- Performance improvements: Converter's
convert(),isConversionSupported(),isSimpleTypeConversion()are faster via improved caching.
3.1.0
- TypeUtilities added. Advanced Java type introspection and generic resolution utilities.
- Currency and Pattern support added to Converter.
- Performance improvements: ClassUtilities caches the results of distance between classes and fetching all supertypes.
- Bug fix: On certain windows machines, applications would not exit because of non-daenmon thread used for scheduler in LRUCache/TTLCache. Fixed by @kpartlow.
3.0.3
java.sql.Dateconversion - considered a timeless "date", like a birthday, and not shifted due to time zones. Example,2025-02-07T23:59:59[America/New_York]coverage effective date, will remain2025-02-07when converted to any time zone.Currencyconversions added (toString, toMap and vice-versa)Patternconversions added (toString, toMap and vice-versa)YearMonthconversions added (all date-time types toYearMonth)Yearconversions added (all date-time types toYear)MonthDayconversions added (all date-time types toMonthDay)- All Temporal classes, when converted to a Map, will typically use a single String to represent the Temporal object. Uses the ISO 8601 formats for dates, other ISO formats for Currency, etc.
3.0.2
- Conversion test added that ensures all conversions go from instance, to JSON, and JSON, back to instance, through all conversion types supported.
java-utilusesjson-ioas a test dependency only.Timestampconversion improvements (better honoring of nanos) and Timezone is always specified now, so no risk of system default Timezone being used. Would only use system default timezone if tz not specified, which could only happen if older version sending older format JSON.
3.0.1
- ClassUtilities adds
Set<Class<?>> findLowestCommonSupertypes(Class<?> a, Class<?> b)
- which returns the lowest common anscestor(s) of two classes, excluding
Object.class.This is useful for finding the common ancestor of two classes that are not related by inheritance. Generally, executes in O(n log n) - uses sort internally. If more than one exists, you can filter the returned Set as you please, favoring classes, interfaces, etc.Class<?> findLowestCommonSupertype(Class<?> a, Class<?> b)
- which is a convenience method that calls the above method and then returns the first one in the Set or null.
boolean haveCommonAncestor(Class<?> a, Class<?> b)
- which returns true if the two classes have a common ancestor (excluding
Object.class).Set<Class<?>> getAllSupertypes(Class<?> clazz)
- which returns all superclasses and interfaces of a class, including itself. This is useful for finding all the classes and interfaces that a class implements or extends.
- Moved
Sealable*test cases to json-io project.- Removed remaining usages of deprecated
CompactLinkedMap.
3.0.0
- DeepEquals now outputs the first encountered graph "diff" in the passed in input/output options Map if provided. See userguide for example output.
- CompactMap and CompactSet no longer do you need to sublcass for variations. Use the new builder api.
- ClassUtilities added
newInstance(). Also,getClassLoader()works in OSGi, JPMS, and non-modular environments.- Converter added support for arrays to collections, arrays to arrays (for type difference that can be converted), for n-dimensional arrays. Collections to arrays and Collections to Collections, also supported nested collections. Arrays and Collections to EnumSet.
- ReflectionUtils robust caching in all cases, optional
Fieldfiltering viaPredicate.- SystemUtilities added many new APIs.
- Traverser updated to support passing all fields to visitor, uses lambda for visitor.
- Should be API compatible with 2.x.x versions.
- Complete Javadoc upgrade throughout the project.
- New User Guide added.
2.18.0
- Fix issue with field access
ClassUtilities.getClassLoader()when in OSGi environment. Thank you @ozhelezniak-talend.- Added
ClassUtilities.getClassLoader(Class<?> c)so that class loading was not confined to java-util classloader bundle. Thank you @ozhelezniak-talend.
2.17.0
ClassUtilities.getClassLoader()added. This will safely return the correct class loader when running in OSGi, JPMS, or neither.ArrayUtilities.createArray()added. This method accepts a variable number of arguments and returns them as an array of typeT[].- Fixed bug when converting
Mapcontaining "time" key (and nodatenorzonekeys) with value tojava.sql.Date.The millisecond portion was set to 0.
2.16.0
SealableMap, LRUCache,andTTLCacheupdated to useConcurrentHashMapNullSafeinternally, to simplify their implementation, as they no longer have to implement the null-safe work,ConcurrentHashMapNullSafedoes that for them.- Added
ConcurrentNavigableMapNullSafeandConcurrentNavigableSetNullSafe- Allow for
SealableNavigableMapandSealableNavigableSetto handle null- Added support for more old timezone names (EDT, PDT, ...)
- Reverted back to agrona 1.22.0 (testing scope only) because it uses class file format 52, which still works with JDK 1.8
- Missing comma in OSGI support added in pom.xml file. Thank you @ozhelezniak.
TestGraphComparator.testNewArrayElementupdated to reliable compare results (not depdendent on a Map that could return items in differing order). Thank you @wtrazs
2.15.0
- Introducing
TTLCache: a cache with a configurable minimum Time-To-Live (TTL). Entries expire and are automatically removed after the specified TTL. Optionally, set amaxSizeto enable Least Recently Used (LRU) eviction. EachTTLCacheinstance can have its own TTL setting, leveraging a sharedScheduledExecutorServicefor efficient resource management. To ensure proper cleanup, callTTLCache.shutdown()when your application or service terminates.- Introducing
ConcurrentHashMapNullSafe: a drop-in replacement forConcurrentHashMapthat supportsnullkeys and values. It uses internal sentinel values to managenulls,providing a seamless experience. This frees users fromnullhandling concerns, allowing unrestricted key-value insertion and retrieval.LRUCacheupdated to use a singleScheduledExecutorServiceacross all instances, regardless of the individual time settings. Call the staticshutdown()method onLRUCachewhen your application or service is ending.
2.14.0
ClassUtilities.addPermanentClassAlias()- add an alias that.forName()can use to instantiate class (e.g. "date" forjava.util.Date)ClassUtilities.removePermanentClassAlias()- remove an alias that.forName()can no longer use.- Updated build plug-in dependencies.
2.13.0
LRUCacheimproved garbage collection handling to avoid gc Nepotism issues by nulling out node references upon eviction. Pointed out by Ben Manes.- Combined
ForkedJoinPoolandScheduledExecutorServiceinto use of onlyScheduledExecutorServive,which is easier for user. The user can supplynullor their own scheduler. In the case ofnull, one will be created and theshutdown()method will terminate it. If the user supplies aScheduledExecutorServiceit will be used, but not shutdown when theshutdown()method is called. This allowsLRUCacheto work well in containerized environments.
2.12.0
LRUCacheupdated to support both "locking" and "threaded" implementation strategies.
2.11.0
LRUCachere-written so that it operates in O(1) forget(),put(),andremove()methods without thread contention. When items are placed into (or removed from) the cache, it schedules a cleanup task to trim the cache to its capacity. This means that it will operate as fast as aConcurrentHashMap,yet shrink to capacity quickly after modifications.
2.10.0
- Fixed potential memory leak in
LRUCache.- Added
nextPermutationtoMathUtilities.- Added
size(),,isEmpty(),andhasContenttoCollectionUtilities.
2.9.0
- Added
SealableListwhich provides aList(orListwrapper) that will make it read-only (sealed) or read-write (unsealed), controllable via aSupplier<Boolean>.This moves the immutability control outside the list and ensures that all views on theListrespect the sealed-ness. One master supplier can control the immutability of many collections.- Added
SealableSetsimilar to SealableList but withSetnature.- Added
SealableMapsimilar to SealableList but withMapnature.- Added
SealableNavigableSetsimilar to SealableList but withNavigableSetnature.- Added
SealableNavigableMapsimilar to SealableList but withNavigableMapnature.- Updated
ConcurrentListto support wrapping anyListand making it thread-safe, including all view APIs:iterator(),listIterator(),listIterator(index).The no-arg constructor creates aConcurrentListready-to-go. The constructor that takes aListparameter constructor wraps the passed in list and makes it thread-safe.- Renamed
ConcurrentHashSettoConcurrentSet.
2.8.0
- Added
ClassUtilities.doesOneWrapTheOther()API so that it is easy to test if one class is wrapping the other.- Added
StringBuilderandStringBuffertoStringsto theConverter.Eliminates special cases for.toString()calls where generalizedconvert(src, type)is being used.
2.7.0
- Added
ConcurrentList,which implements a thread-safeList.Provides all API support except forlistIterator(),however, it implementsiterator()which returns an iterator to a snapshot copy of theList.- Added
ConcurrentHashSet,a trueSetwhich is a bit easier to use thanConcurrentSkipListSet,which as aNavigableSetandSortedSet,requires each element to beComparable.- Performance improvement: On
LRUCache,removed unnecessaryCollections.SynchronizedMapsurrounding the internalLinkedHashMapas the concurrent protection offered byReentrantReadWriteLockis all that is needed.
2.6.0
- Performance improvement:
Converterinstance creation is faster due to the code no longer copying the static default table. Overrides are kept in separate variable.- New capability added:
MathUtilities.parseToMinimalNumericType()which will parse a String number into a Long, BigInteger, Double, or BigDecimal, choosing the "smallest" datatype to represent the number without loss of precision.- New conversions added to convert from
MaptoStringBuilderandStringBuffer.
2.5.0
- pom.xml file updated to support both OSGi Bundle and JPMS Modules.
- module-info.class resides in the root of the .jar but it is not referenced.
2.4.9
- Updated to allow the project to be compiled by versions of JDK > 1.8 yet still generate class file format 52 .class files so that they can be executed on JDK 1.8+ and up.
- Incorporated @AxataDarji GraphComparator changes that reduce cyclomatic code complexity (refactored to smaller methods)
2.4.8
- Performance improvement:
DeepEquals.deepHashCode()- now usingIdentityHashMap()for cycle (visited) detection.- Modernization:
UniqueIdGenerator- updated to useLock.lock()andLock.unlock()instead ofsynchronizedkeyword.- Using json-io 4.14.1 for cloning object in "test" scope, eliminates cycle depedencies when building both json-io and java-util.
2.4.7
- All 687 conversions supported are now 100% cross-product tested. Converter test suite is complete.
2.4.6
- All 686 conversions supported are now 100% cross-product tested. There will be more exception tests coming.
2.4.5
- Added
ReflectionUtils.getDeclaredFields()which gets fields from aClass, including anEnum, and special handles enum so that system fields are not returned.
2.4.4
Converter- Enum test added. 683 combinations.
2.4.3
DateUtilities- now supports timezone offset with seconds component (rarer than seeing a bald eagle in your backyard).Converter- many more tests added...682 combinations.
2.4.2
- Fixed compatibility issues with
StringUtilities.Method parameters changed from String to CharSequence broke backward compatibility. Linked jars are bound to method signature at compile time, not at runtime. Added both methods where needed. Removed methods with "Not" in the name.- Fixed compatibility issue with
FastByteArrayOutputStream.The.getBuffer()API was removed in favor of toByteArray(). Now both methods exist, leavinggetBuffer()for backward compatibility.- The Converter "Everything" test updated to track which pairs are tested (fowarded or reverse) and then outputs in order what tests combinations are left to write.
2.4.1
Converterhas had significant expansion in the types that it can convert between, about 670 combinations. In addition, you can add your own conversions to it as well. Call theConverter.getSupportedConversions()to see all the combinations supported. Also, you can useConverterinstance-based now, allowing it to have different conversion tables if needed.DateUtilitieshas had performance improvements (> 35%), and adds a new.parseDate()API that allows it to return aZonedDateTime.See the updated Javadoc on the class for a complete description of all the formats it supports. Normally, you do not need to use this class directly, as you can useConverterto convert betweenDates,Calendars, and the new Temporal classes likeZonedDateTime,Duration,Instance,as well as Strings.FastByteArrayOutputStreamupdated to matchByteArrayOutputStreamAPI. This means that.getBuffer()is.toByteArray()and.clear()is now.reset().FastByteArrayInputStreamadded. MatchesByteArrayInputStreamAPI.- Bug fix:
SafeSimpleDateFormatto properly format dates having years with fewer than four digits.- Bug fix: SafeSimpleDateFormat .toString(), .hashCode(), and .equals() now delegate to the contain SimpleDataFormat instance. We recommend using the newer DateTimeFormatter, however, this class works well for Java 1.8+ if needed.
2.4.0
- Added ClassUtilities. This class has a method to get the distance between a source and destination class. It includes support for Classes, multiple inheritance of interfaces, primitives, and class-to-interface, interface-interface, and class to class.
- Added LRUCache. This class provides a simple cache API that will evict the least recently used items, once a threshold is met.
2.3.0
Added
FastReaderandFastWriter.
FastReadercan be used instead of the JDKPushbackReader(BufferedReader)).It is much faster with no synchronization and combines both. It also tracks line[getLine()]and column[getCol()]position monitoring for0x0awhich it can be queried for. It also can be queried for the last snippet read:getLastSnippet().Great for showing parsing error messages that accurately point out where a syntax error occurred. Make sure you use a new instance per each thread.FastWritercan be used instead of the JDKBufferedWriteras it has no synchronization. Make sure you use a new Instance per each thread.
2.2.0
- Built with JDK 1.8 and runs with JDK 1.8 through JDK 21.
- The 2.2.x will continue to maintain JDK 1.8. The 3.0 branch [not yet created] will be JDK11+
- Added tests to verify that
GraphComparatorandDeepEqualsdo not count sorted order of Sets for equivalency. It does however, requireCollectionsthat are notSetsto be in order.
2.1.1
- ReflectionUtils skips static fields, speeding it up and remove runtime warning (field SerialVersionUID). Supports JDK's up through 21.
2.1.0
DeepEquals.deepEquals(a, b)compares Sets and Maps without regards to order per the equality spec.- Updated all dependent libraries to latest versions as of 16 Sept 2023.
2.0.0
- Upgraded from Java 8 to Java 11.
- Updated
ReflectionUtils.getClassNameFromByteCode()to handle up to Java 17classfile format.
1.68.0
- Fixed:
UniqueIdGeneratornow correctly gets last two digits of ID using 3 attempts - JAVA_UTIL_CLUSTERID (optional), CF_INSTANCE_INDEX, and finally using SecuritRandom for the last two digits.- Removed
log4jin favor ofslf4jandlogback.
1.67.0
- Updated log4j dependencies to version
2.17.1.
1.66.0
- Updated log4j dependencies to version
2.17.0.
1.65.0
- Bug fix: Options (IGNORE_CUSTOM_EQUALS and ALLOW_STRINGS_TO_MATCH_NUMBERS) were not propagated inside containers\
- Bug fix: When progagating options the Set of visited ItemsToCompare (or a copy if it) should be passed on to prevent StackOverFlow from occurring.
1.64.0
- Performance Improvement:
DateUtilitiesnow using non-greedy matching for regex's within date sub-parts.- Performance Improvement:
CompactMapupdated to use non-copying iterator for all non-Sorted Maps.- Performance Improvement:
StringUtilities.hashCodeIgnoreCase()slightly faster - calls JDK method that makes one less call internally.
1.63.0
- Performance Improvement: Anytime
CompactMap/CompactSetis copied internally, the destination map is pre-sized to correct size, eliminating growing underlying Map more than once.ReflectionUtils.getConstructor()added. Fetches Constructor, caches reflection operation - 2nd+ calls pull from cache.
1.62.0
- Updated
DateUtilitiesto handle sub-seconds precision more robustly.- Updated
GraphComparatorto add missing srcValue when MAP_PUT replaces existing value. @marcobjorge
1.61.0
Converternow supportsLocalDate,LocalDateTime,ZonedDateTimeto/fromCalendar,Date,java.sql.Date,Timestamp,Long,BigInteger,BigDecimal,AtomicLong,LocalDate,LocalDateTime, andZonedDateTime.
1.60.0 [Java 1.8+]
- Updated to require Java 1.8 or newer.
UniqueIdGeneratorwill recognize Cloud FoundryCF_INSTANCE_INDEX, in addition toJAVA_UTIL_CLUSTERIDas an environment variable or Java system property. This will be the last two digits of the generated unique id (making it cluster safe). Alternatively, the value can be the name of another environment variable (detected by not being parseable as an int), in which case the value of the specified environment variable will be parsed as server id within cluster (value parsed as int, mod 100).- Removed a bunch of Javadoc warnings from build.
1.53.0 [Java 1.7+]
- Updated to consume
log4j 2.13.3- more secure.
1.52.0
ReflectionUtilsnow caches the methods it finds byClassLoaderandClass. Earlier, found methods were cached perClass. This did not handle the case when multipleClassLoaderswere used to load the same class with the same method. UsingReflectionUtilsto locate thefoo()method will find it inClassLoaderX.ClassA.foo()(and cache it as such), and if asked to find it inClassLoaderY.ClassA.foo(),ReflectionUtilswill not find it in the cache withClassLoaderX.ClassA.foo(), but it will fetch it fromClassLoaderY.ClassA.foo()and then cache the method with thatClassLoader/Classpairing.DeepEquals.equals()was not comparingBigDecimalscorrectly. If they had different scales but represented the same value, it would returnfalse. Now they are properly compared usingbd1.compareTo(bd2) == 0.DeepEquals.equals(x, y, options)has a new option. If you addALLOW_STRINGS_TO_MATCH_NUMBERSto the options map, then if aStringis being compared to aNumber(or vice-versa), it will convert theStringto aBigDecimaland then attempt to see if the values still match. If so, then it will continue. If it could not convert theStringto aNumber, or the convertedStringas aNumberdid not match,falseis returned.convertToBigDecimal()now handles very largelongsandAtomicLongscorrectly (before it returnedfalseif thelongswere greater than adouble'smax integer representation.)CompactCIHashSetandCompactCILinkedHashSetnow return a newMapthat is sized tocompactSize() + 1when switching from internal storage toHashSet/LinkedHashSetfor storage. This is purely a performance enhancement.
1.51.0
New Sets:
CompactCIHashSetadded. ThisCompactSetexpands to a case-insensitiveHashSetwhensize() > compactSize().CompactCILinkedSetadded. ThisCompactSetexpands to a case-insensitiveLinkedHashSetwhensize() > compactSize().CompactLinkedSetadded. ThisCompactSetexpands to aLinkedHashSetwhensize() > compactSize().CompactSetexists. ThisCompactSetexpands to aHashSetwhensize() > compactSize().New Maps:
CompactCILinkedMapexists. ThisCompactMapexpands to a case-insensitiveLinkedHashMapwhensize() > compactSize()entries.CompactCIHashMapexists. ThisCompactMapexpands to a case-insensitiveHashMapwhensize() > compactSize()entries.CompactLinkedMapadded. ThisCompactMapexpands to aLinkedHashMapwhensize() > compactSize()entries.CompactMapexists. ThisCompactMapexpands to aHashMapwhensize() > compactSize()entries.
1.50.0
CompactCIHashMapadded. This is aCompactMapthat is case insensitive. When more thancompactSize()entries are stored in it (default 50), it uses aCaseInsenstiveMapHashMapto hold its entries.CompactCILinkedMapadded. This is aCompactMapthat is case insensitive. When more thancompactSize()entries are stored in it (default 50), it uses aCaseInsenstiveMapLinkedHashMapto hold its entries.- Bug fix:
CompactMapentrySet()andkeySet()were not handling theretainAll(),containsAll(), andremoveAll()methods case-insensitively when case-insensitivity was activated.Convertermethods that convert to byte, short, int, and long now accepted String decimal numbers. The decimal portion is truncated.
1.49.0
- Added
CompactSet. Works similarly toCompactMapwith singleObject[]holding elements until it crossescompactSize()threshold. ThisObject[]is adjusted dynamically as objects are added and removed.
1.48.0
- Added
charandCharactersupport toConvert.convert*()- Added full Javadoc to
Converter.- Performance improvement in
Iterator.remove()for all ofCompactMap'siterators:keySet().iterator(),entrySet().iterator, andvalues().iterator.- In order to get to 100% code coverage with Jacoco, added more tests for
Converter,CaseInsenstiveMap, andCompactMap.
1.47.0
Converter.convert2*()methods added: Ifnullpassed in, primitive 'logical zero' is returned. Example:Converter.convert(null, boolean.class)returnsfalse.Converter.convertTo*()methods: ifnullpassed in,nullis returned. Allows "tri-state" Boolean. Example:Converter.convert(null, Boolean.class)returnsnull.Converter.convert()converts usingconvertTo*()methods for primitive wrappers, andconvert2*()methods for primitive classes.Converter.setNullMode()removed.
1.46.0
CompactMapnow supports 4 stages of "growth", making it much smaller in memory than nearly anyMap. After0and1entries, and between2andcompactSize()entries, the entries in theMapare stored in anObject[](using same single member variable). The even elements the 'keys' and the odd elements are the associated 'values'. This array is dynamically resized to exactly match the number of stored entries. When more thancompactSize()entries are used, theMapthen uses theMapreturned from the overrideablegetNewMap()api to store the entries. In all cases, it maintains the underlying behavior of theMap.- Updated to consume
log4j 2.13.1
1.45.0
CompactMapnow supports case-insensitivity when using String keys. By default, it is case sensitive, but you can override theisCaseSensitive()method and returnfalse. This allows you to returnTreeMap(String.CASE_INSENSITIVE_ORDER)orCaseInsensitiveMapfrom thegetNewMap()method. With these overrides, CompactMap is now case insensitive, yet still 'compact.'Converter.setNullMode(Converter.NULL_PROPER | Converter.NULL_NULL)added to allow control over hownullvalues are converted. By default, passing anullvalue into primitiveconvert*()methods returns the primitive form of0orfalse. If the static methodConverter.setNullMode(Converter.NULL_NULL)is called it will change the behavior of the primitiveconvert*()methods returnnull.
1.44.0
CompactMapintroduced.CompactMapis aMapthat strives to reduce memory at all costs while retaining speed that is close toHashMap'sspeed. It does this by using only one (1) member variable (of typeObject) and changing it as theMapgrows. It goes from single value, to a singleMap Entry, to anObject[], and finally it uses aMap(user defined).CompactMapis especially small when0or1entries are stored in it. Whensize()is from2tocompactSize(), then entries are stored internally in singleObject[]. If thesize() > compactSize()then the entries are stored in a regularMap.// If this key is used and only 1 element then only the value is stored protected K getSingleValueKey() { return "someKey"; } // Map you would like it to use when size() > compactSize(). HashMap is default protected abstract Map<K, V> getNewMap(); // If you want case insensitivity, return true and return new CaseInsensitiveMap or TreeMap(String.CASE_INSENSITIVE_PRDER) from getNewMap() protected boolean isCaseInsensitive() { return false; } // 1.45.0 // When size() > than this amount, the Map returned from getNewMap() is used to store elements. protected int compactSize() { return 100; } // 1.46.0Empty
This class only has one (1) member variable of type
Object. If there are no entries in it, then the value of that member variable takes on a pointer (points to sentinel value.)One entry
If the entry has a key that matches the value returned from
getSingleValueKey()then there is no key stored and the internal single member points to the value (still retried with 100% proper Map semantics).If the single entry's key does not match the value returned from
getSingleValueKey()then the internal field points to an internalClassCompactMapEntrywhich contains the key and the value (nothing else). Again, all APIs still operate the same.2 thru compactSize() entries
In this case, the single member variable points to a single Object[] that contains all the keys and values. The keys are in the even positions, the values are in the odd positions (1 up from the key). [0] = key, [1] = value, [2] = next key, [3] = next value, and so on. The Object[] is dynamically expanded until size() > compactSize(). In addition, it is dynamically shrunk until the size becomes 1, and then it switches to a single Map Entry or a single value.
size() > compactSize()
In this case, the single member variable points to a
Mapinstance (supplied bygetNewMap()API that user supplied.) This allowsCompactMapto work with nearly allMaptypes. This Map supports null for the key and values, as long as the Map returned by getNewMap() supports null keys-values.
1.43.0
CaseInsensitiveMap(Map orig, Map backing)added for allowing precise control of whatMapinstance is used to back theCaseInsensitiveMap. For example,Map originalMap = someMap // has content already in it Map ciMap1 = new CaseInsensitiveMap(someMap, new TreeMap()) // Control Map type, but not initial capacity Map ciMap2 = new CaseInsensitiveMap(someMap, new HashMap(someMap.size())) // Control both Map type and initial capacity Map ciMap3 = new CaseInsensitiveMap(someMap, new Object2ObjectOpenHashMap(someMap.size())) // Control initial capacity and use specialized Map from fast-util.
CaseInsensitiveMap.CaseInsensitiveString()constructor madepublic.
1.42.0
CaseInsensitiveMap.putObject(Object key, Object value)added for placing objects into typed Maps.
1.41.0
CaseInsensitiveMap.plus()and.minus()added to support+and-operators in languages like Groovy.CaseInsenstiveMap.CaseInsensitiveString(staticinner Class) is nowpublic.
1.40.0
- Added
ReflectionUtils.getNonOverloadedMethod()to support reflectively fetching methods with only Class and Method name available. This implies there is no method overloading.
1.39.0
- Added
ReflectionUtils.call(bean, methodName, args...)to allow one-step reflective calls. See Javadoc for any limitations.- Added
ReflectionUtils.call(bean, method, args...)to allow easy reflective calls. This version requires obtaining theMethodinstance first. This approach allows methods with the same name and number of arguments (overloaded) to be called.- All
ReflectionUtils.getMethod()APIs cache reflectively located methods to significantly improve performance when using reflection.- The
call()methods throw the target of the checkedInvocationTargetException. The checkedIllegalAccessExceptionis rethrown wrapped in a RuntimeException. This allows making reflective calls without having to handle these two checked exceptions directly at the call point. Instead, these exceptions are usually better handled at a high-level in the code.
1.38.0
- Enhancement:
UniqueIdGeneratornow generates the long ids in monotonically increasing order. @HonorKnight- Enhancement: New API [
getDate(uniqueId)] added toUniqueIdGeneratorthat when passed an ID that it generated, will return the time down to the millisecond when it was generated.
1.37.0
TestUtil.assertContainsIgnoreCase()andTestUtil.checkContainsIgnoreCase()APIs added. These are generally used in unit tests to check error messages for key words, in order (as opposed to doing.contains()on a string which allows the terms to appear in any order.)- Build targets classes in Java 1.7 format, for maximum usability. The version supported will slowly move up, but only based on necessity allowing for widest use of java-util in as many projects as possible.
1.36.0
Converter.convert()now bi-directionally supportsCalendar.class, e.g. Calendar to Date, SqlDate, Timestamp, String, long, BigDecimal, BigInteger, AtomicLong, and vice-versa.UniqueIdGenerator.getUniqueId19()is a new API for getting 19 digit unique IDs (a fulllongvalue) These are generated at a faster rate (10,000 per millisecond vs. 1,000 per millisecond) than the original (18-digit) API.- Hardcore test added for ensuring concurrency correctness with
UniqueIdGenerator.- Javadoc beefed up for
UniqueIdGenerator.- Updated public APIs to have proper support for generic arguments. For example Class<T>, Map<?, ?>, and so on. This eliminates type casting on the caller's side.
ExceptionUtilities.getDeepestException()added. This API locates the source (deepest) exception.
1.35.0
DeepEquals.deepEquals(), when comparingMaps, theMap.Entrytype holding theMap'sentries is no longer considered in equality testing. In the past, a custom Map.Entry instance holding the key and value could cause inquality, which should be ignored. @AndreyNudkoConverter.convert()now uses parameterized types so that the return type matches the passed inClassparameter. This eliminates the need to cast the return value ofConverter.convert().MapUtilities.getOrThrow()added which throws the passed inThrowablewhen the passed in key is not within theMap. @ptjuanramos
1.34.2
- Performance Improvement:
CaseInsensitiveMap, when created from anotherCaseInsensitiveMap, re-uses the internalCaseInsensitiveStringkeys, which are immutable.- Bug fix:
Converter.convertToDate(), Converter.convertToSqlDate(), and Converter.convertToTimestamp()all threw aNullPointerExceptionif the passed in content was an empty String (of 0 or more spaces). When passed in NULL to these APIs, you get back null. If you passed in empty strings or bad date formats, an IllegalArgumentException is thrown with a message clearly indicating what input failed and why.
1.34.0
- Enhancement:
DeepEquals.deepEquals(a, b options)added. The new options map supports a keyDeepEquals.IGNORE_CUSTOM_EQUALSwhich can be set to a Set of String class names. If any of the encountered classes in the comparison are listed in the Set, and the class has a custom.equals()method, it will not be called and instead adeepEquals()will be performed. If the value associated to theIGNORE_CUSTOM_EQUALSkey is an empty Set, then no custom.equals()methods will be called, except those on primitives, primitive wrappers,Date,Class, andString.
1.33.0
- Bug fix:
DeepEquals.deepEquals(a, b)could report equivalent unorderedCollections/Mapsas not equal if the items in theCollection/Maphad the same hash code.
1.32.0
Converterupdated to exposeconvertTo*()APIs that allow converting to a known type.
1.31.1
- Renamed
AdjustableFastGZIPOutputStreamtoAdjustableGZIPOutputStream.
1.31.0
- Add
AdjustableFastGZIPOutputStreamso that compression level can be adjusted.
1.30.0
ByteArrayOutputStreamsconverted toFastByteArrayOutputStreamsinternally.
1.29.0
- Removed test dependencies on Guava
- Rounded out APIs on
FastByteArrayOutputStream- Added APIs to
IOUtilities.
1.28.2
- Enhancement:
IOUtilities.compressBytes(FastByteArrayOutputStream, FastByteArrayOutputStream)added.
1.28.1
- Enhancement:
FastByteArrayOutputStream.getBuffer()API made public.
1.28.0
- Enhancement:
FastByteArrayOutputStreamadded. Similar to JDK class, but withoutsynchronizedand access to innerbyte[]allowed without duplicating thebyte[].
1.27.0
- Enhancement:
Converter.convert()now supportsenumtoString
1.26.1
- Bug fix: The internal class
CaseInsensitiveStringdid not implementComparableinterface correctly.
1.26.0
- Enhancement: added
getClassNameFromByteCode()API toReflectionUtils.
1.25.1
- Enhancement: The Delta object returned by
GraphComparatorimplementsSerializablefor those usingObjectInputStream/ObjectOutputStream. Provided by @metlaivan (Ivan Metla)
1.25.0
- Performance improvement:
CaseInsensitiveMap/Setinternally addsStringstoMapwithout using.toLowerCase()which eliminates creating a temporary copy on the heap of theStringbeing added, just to get its lowerCaseValue.- Performance improvement:
CaseInsensitiveMap/Setuses less memory internally by caching the hash code as anint, instead of anInteger.StringUtilities.caseInsensitiveHashCode()API added. This allows computing a case-insensitive hashcode from aStringwithout any object creation (heap usage).
1.24.0
Converter.convert()- performance improved using class instance comparison versus classStringname comparison.CaseInsensitiveMap/Set- performance improved.CaseInsensitiveString(internal) short-circuits on equality check if hashCode() [cheap runtime cost] is not the same. Also, all method returning true/false to detect ifSetorMapchanged rely on size() instead of contains.
1.23.0
Converter.convert()API update: When a mutable type (Date,AtomicInteger,AtomicLong,AtomicBoolean) is passed in, and the destination type is the same, rather than return the instance passed in, a copy of the instance is returned.
1.22.0
- Added
GraphComparatorwhich is used to compute the difference (delta) between two object graphs. The generatedListof Delta objects can be 'played' against the source to bring it up to match the target. Very useful in transaction processing systems.
1.21.0
- Added
Executorwhich is used to execute Operating System commands. For example,Executor exector = new Executor(); executor.exec("echo This is handy"); assertEquals("This is handy", executor.getOut().trim());- bug fix:
CaseInsensitiveMap, when passed aLinkedHashMap, was inadvertently using a HashMap instead.
1.20.5
CaseInsensitiveMapintentionally does not retain 'not modifiability'.CaseInsensitiveSetintentionally does not retain 'not modifiability'.
1.20.4
- Failed release. Do not use.
1.20.3
TrackingMapchanged so thatget(anyKey)always marks it as keyRead. Same forcontainsKey(anyKey).CaseInsensitiveMaphas a constructor that takes aMap, which allows it to take on the nature of theMap, allowing for case-insensitiveConcurrentHashMap, sortedCaseInsensitiveMap, etc. The 'Unmodifiable'Mapnature is intentionally not taken on. The passed inMapis not mutated.CaseInsensitiveSethas a constructor that takes aCollection, which allows it to take on the nature of theCollection, allowing for sortedCaseInsensitiveSets. The 'unmodifiable'Collectionnature is intentionally not taken on. The passed inSetis not mutated.
1.20.2
TrackingMapchanged so that an existing key associated to null counts as accessed. It is valid for manyMaptypes to allow null values to be associated to the key.TrackingMap.getWrappedMap()added so that you can fetch the wrappedMap.
1.20.1
TrackingMapchanged so that.put()does not mark the key as accessed.
1.20.0
TrackingMapadded. Create this map around any type of Map, and it will track which keys are accessed via .get(), .containsKey(), or .put() (when put overwrites a value already associated to the key). Provided by @seankellner.
1.19.3
- Bug fix:
CaseInsensitiveMap.entrySet()- callingentry.setValue(k, v)while iterating the entry set, was not updating the underlying value. This has been fixed and test case added.
1.19.2
- The order in which system properties are read versus environment variables via the
SystemUtilities.getExternalVariable()method has changed. System properties are checked first, then environment variables.
1.19.1
- Fixed issue in
DeepEquals.deepEquals()where a Container type (MaporCollection) was being compared to a non-container - the result of this comparison was inconsistent. It is always false if a Container is compared to a non-container type (anywhere within the object graph), regardless of the comparison order A, B versus comparing B, A.
1.19.0
StringUtilities.createUtf8String(byte[])API added which is used to easily create UTF-8 strings without exception handling code.StringUtilities.getUtf8Bytes(String s)API added which returns a byte[] of UTF-8 bytes from the passed in Java String without any exception handling code required.ByteUtilities.isGzipped(bytes[])API added which returns true if thebyte[]represents gzipped data.IOUtilities.compressBytes(byte[])API added which returns the gzipped version of the passed inbyte[]as abyte[]IOUtilities.uncompressBytes(byte[])API added which returns the original byte[] from the passed in gzippedbyte[].- JavaDoc issues correct to support Java 1.8 stricter JavaDoc compilation.
1.18.1
UrlUtilitiesnow allows for per-threaduserAgentandreferreras well as maintains backward compatibility for setting these values globally.StringUtilitiesgetBytes()andcreateString()now allow null as input, and return null for output for null input.- Javadoc updated to remove errors flagged by more stringent Javadoc 1.8 generator.
1.18.0
- Support added for
TimestampinConverter.convert()nullcan be passed intoConverter.convert()for primitive types, and it will return their logical 0 value (0.0f, 0.0d, etc.). For primitive wrappers, atomics, etc, null will be returned.- "" can be passed into
Converter.convert()and it will set primitives to 0, and the object types (primitive wrappers, dates, atomics) to null.Stringwill be set to "".
1.17.1
- Added full support for
AtomicBoolean,AtomicInteger, andAtomicLongtoConverter.convert(value, AtomicXXX). Any reasonable value can be converted to/from these, including Strings, Dates (AtomicLong), allNumbertypes.IOUtilities.flush()now supportsXMLStreamWriter
1.17.0
UIUtilities.close()now supportsXMLStreamReaderandXMLStreamWriterin addition toCloseable.Converter.convert(value, type)- a value of null is supported for the numeric types, boolean, and the atomics - in which case it returns their "zero" value and false for boolean. For date and String return values, a null input will return null. Thetypeparameter must not be null.
1.16.1
- In
Converter.convert(value, type), the value is trimmed of leading / trailing white-space if it is a String and the type is aNumber.
1.16.0
- Added
Converter.convert()API. Allows converting instances of one type to another. Handles all primitives, primitive wrappers,Date,java.sql.Date,String,BigDecimal,BigInteger,AtomicInteger,AtomicLong, andAtomicBoolean. Additionally, input (from) argument acceptsCalendar.- Added static
getDateFormat()toSafeSimpleDateFormatfor quick access to thread local formatter (per formatString).
1.15.0
- Switched to use Log4J2 () for logging.
1.14.1
- bug fix:
CaseInsensitiveMap.keySet()was only initializing the iterator once. IfkeySet()was called a 2nd time, it would no longer work.
1.14.0
- bug fix:
CaseInsensitiveSet(), the return value foraddAll(),returnAll(), andretainAll()was wrong in some cases.
1.13.3
EncryptionUtilities- Added byte[] APIs. Makes it easy to encrypt/decryptbyte[]data.pom.xmlhad extraneous characters inadvertently added to the file - these are removed.- 1.13.1 & 13.12 - issues with sonatype
1.13.0
DateUtilities- Day of week allowed (properly ignored).DateUtilities- First (st), second (nd), third (rd), and fourth (th) ... supported.DateUtilities- The default toString() standard date / time displayed by the JVM is now supported as a parseable format.DateUtilities- Extra whitespace can exist within the date string.DateUtilities- Full time zone support added.DateUtilities- The date (or date time) is expected to be in isolation. Whitespace on either end is fine, however, once the date time is parsed from the string, no other content can be left (prevents accidently parsing dates from dates embedded in text).UrlUtilities- Removed proxy from calls toURLUtilities. These are now done through the JVM.
1.12.0
UniqueIdGeneratoruses 99 as the cluster id when the JAVA_UTIL_CLUSTERID environment variable or System property is not available. This speeds up execution on developer's environments when they do not specifyJAVA_UTIL_CLUSTERID.- All the 1.11.x features rolled up.
1.11.3
UrlUtilities- separated out call that resolvesres://to a public API to allow for wider use.
1.11.2
- Updated so headers can be set individually by the strategy (
UrlInvocationHandler)InvocationHandlerset to always usesPOSTmethod to allow additionalHTTPheaders.
1.11.1
- Better IPv6 support (
UniqueIdGenerator)- Fixed
UrlUtilities.getContentFromUrl()(byte[]) no longer setting upSSLFactorywhenHTTPprotocol used.
1.11.0
UrlInvocationHandler,UrlInvocationStrategy- Updated to allow more generalized usage. Pass in your implementation ofUrlInvocationStrategywhich allows you to set the number of retry attempts, fill out the URL pattern, set up the POST data, and optionally set/get cookies.- Removed dependency on json-io. Only remaining dependency is Apache commons-logging.
1.10.0
- Issue #3 fixed:
DeepEquals.deepEquals()allows similarMap(orCollection) types to be compared without returning 'not equals' (false). Example,HashMapandLinkedHashMapare compared on contents only. However, compare aSortedSet(likeTreeMap) toHashMapwould fail unless the Map keys are in the same iterative order.- Tests added for
UrlUtilities- Tests added for
Traverser
1.9.2
- Added wildcard to regex pattern to
StringUtilities. This API turns a DOS-like wildcard pattern (where * matches anything and ? matches a single character) into a regex pattern useful inString.matches()API.
1.9.1
- Floating-point allow difference by epsilon value (currently hard-coded on
DeepEquals. Will likely be optional parameter in future version).
1.9.0
MathUtilitiesadded. Currently, variable lengthminimum(arg0, arg1, ... argn)andmaximum()functions added. Available forlong,double,BigInteger, andBigDecimal. These cover the smaller types.CaseInsensitiveMapandCaseInsensitiveSetkeySet()andentrySet()are faster as they do not make a copy of the entries. Internally,CaseInsensitiveStringcaches it's hash, speeding up repeated access.StringUtilities levenshtein()anddamerauLevenshtein()added to compute edit length. See Wikipedia to understand of the difference between these two algorithms. Currently recommend usinglevenshtein()as it uses less memory.- The Set returned from the
CaseInsensitiveMap.entrySet()now contains mutable entry's (value-side). It had been using an immutable entry, which disallowed modification of the value-side during entry walk.
1.8.4
UrlUtilities, fixed issue where the default settings for the connection were changed, not the settings on the actual connection.
1.8.3
ReflectionUtilitieshas newgetClassAnnotation(classToCheck, annotation)API which will return the annotation if it exists within the classes super class hierarchy or interface hierarchy. Similarly, thegetMethodAnnotation()API does the same thing for method annotations (allow inheritance - class or interface).
1.8.2
CaseInsensitiveMapmethodskeySet()andentrySet()return Sets that are identical to how the JDK returns 'view' Sets on the underlying storage. This means that all operations, besidesadd()andaddAll(), are supported.CaseInsensitiveMap.keySet()returns aSetthat is case insensitive (not aCaseInsensitiveSet, just aSetthat ignores case). Iterating thisSetproperly returns each originally stored item.
1.8.1
- Fixed
CaseInsensitiveMap() removeAll()was not removing when accessed viakeySet()
1.8.0
- Added
DateUtilities. See description above.
1.7.4
- Added "res" protocol (resource) to
UrlUtilitiesto allow files from classpath to easily be loaded. Useful for testing.
1.7.2
UrlUtilities.getContentFromUrl() / getContentFromUrlAsString()- removed hard-coded proxy server name
1.7.1
UrlUtilities.getContentFromUrl() / getContentFromUrlAsString()- allow content to be fetched asStringor binary (byte[]).
1.7.0
SystemUtilitiesadded. New API to fetch value from environment or System propertyUniqueIdGenerator- checks for environment variable (or System property) JAVA_UTIL_CLUSTERID (0-99). Will use this if set, otherwise last IP octet mod 100.
1.6.1
- Added:
UrlUtilities.getContentFromUrl()
1.6.0
- Added
CaseInsensitiveSet.
1.5.0
- Fixed:
CaseInsensitiveMap's iterator.remove()method, it did not remove items.- Fixed:
CaseInsensitiveMap's equals()method, it required case to match on keys.
1.4.0
- Initial version