Skip to content

Wait for Kryo 6: ImmutableCollections ListN nulls - #1302

Closed
arimu1 wants to merge 3 commits into
EsotericSoftware:masterfrom
arimu1:fix/1239-listn-nulls
Closed

Wait for Kryo 6: ImmutableCollections ListN nulls#1302
arimu1 wants to merge 3 commits into
EsotericSoftware:masterfrom
arimu1:fix/1239-listn-nulls

Conversation

@arimu1

@arimu1 arimu1 commented Aug 25, 2026

Copy link
Copy Markdown

Summary

This PR is not a Kryo 5 production change.

Stream.toList() can produce ImmutableCollections$ListN instances that contain nulls, but Kryo 5 cannot adopt that reconstruction path:

  • Kryo 5 max source level is JDK 11 (Stream.toList is JDK 16+).
  • Flipping setElementsCanBeNull is not backwards compatible.

Per review on this PR, the production fix waits for Kryo 6. The Kryo 5 serializer/tests were reverted to master (setElementsCanBeNull(false), List.of / List.copyOf). JMH numbers for List.of vs Stream.toList are in the thread: #1302 (comment)

Related: #1239

Test plan

  • Reverted JdkImmutableListSerializer and ImmutableCollectionsSerializersTest to upstream master
  • Removed ImmutableListFactoryBenchmark from this tree (JDK 11 mvn install must not compile Stream.toList)

Stream.toList() can produce ListN instances that contain nulls, but the
immutable list serializer assumed non-null elements and rebuilt lists
with List.of/List.copyOf. Enable null element serialization and rebuild
with stream().toList() per maintainer guidance on EsotericSoftware#1239.

Fixes EsotericSoftware#1239
@theigl

theigl commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR @arimu1. Unfortunately, this doesn't work for 2 reasons:

  • The maximum source level for Kryo 5 is JDK 11 where the toList method on stream does not exist
  • The change is not backwards compatible because of the change to the setElementsCanBeNull flag

We will have to wait for Kryo 6 to merge this.

Also I'd be interested to know how this affects performance. Can you do a benchmark (preferable JMH) and compare the performance of List.of to Stream.toList across some different list sizes?

Requested on EsotericSoftware#1302 for the Kryo 6 reconstruction path, using the existing
benchmarks module. Sizes 0, 1, 2, 10, 100, and 1000.
@arimu1

arimu1 commented Aug 26, 2026

Copy link
Copy Markdown
Author

Thanks @theigl — agreed on both blockers.

  • We will wait for Kryo 6. Kryo 5's max source level is JDK 11, so we will not force Stream.toList() onto this line (toList is JDK 16+; PR CI already fails the JDK 11 compile with cannot find symbol: method toList()).
  • Changing setElementsCanBeNull is not a backwards-compatible path, so that is not a Kryo 5 fix either.

I added ImmutableListFactoryBenchmark in the existing JMH module (List.of vs Stream.toList at sizes 0, 1, 2, 10, 100, 1000). Local run on Temurin 21.0.12, macOS aarch64, -f 2 -wi 3 -i 3 -t 1 -w 1s -r 1s:

size List.of (ops/s) Stream.toList (ops/s) List.of / toList
0 2.35e9 9.11e7 ~26×
1 6.24e8 7.77e7 ~8.0×
2 6.58e8 6.99e7 ~9.4×
10 1.16e8 4.03e7 ~2.9×
100 1.31e7 4.58e6 ~2.9×
1000 1.27e6 4.97e5 ~2.6×

List.of is faster at every size, especially 0/1/2 (empty singleton / List12). From size 10 the gap is about 2.5–3×. Reconstruction is cheap next to element ser/deser, but Stream.toList is clearly more expensive. Happy to re-run with longer forks if useful.

No production rewrite on this Kryo 5 PR.

Kryo 5 cannot use Stream.toList (JDK 16+) and setElementsCanBeNull is not
backwards compatible. Wait for Kryo 6; drop the JMH source from this tree.
@arimu1 arimu1 changed the title Fix ImmutableCollections ListN serialization for null elements Wait for Kryo 6: ImmutableCollections ListN nulls Aug 26, 2026
@arimu1

arimu1 commented Aug 26, 2026

Copy link
Copy Markdown
Author

Closing this Kryo 5 PR: there is no production path here.

Waiting for Kryo 6 (JDK 11 cannot compile Stream.toList; setElementsCanBeNull is not backwards compatible). JMH List.of vs Stream.toList numbers are already in the thread: #1302 (comment)

@arimu1 arimu1 closed this Aug 26, 2026
@theigl

theigl commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Thank you for the benchmark @arimu1!

It is unfortunate that the stream API is so much slower. But I don't see another way to handle this. The only option would be to use reflection to check if the list allows nulls (ListN.allowNulls). But that would make the serializer unsafe and dependent on JDK internals. Another possible option would be to use separate serializers for empty list, List12 and ListN. Empty list and List12 (no nulls allowed) could stay on the more performant path.

I have to revisit this once main is on JDK17+.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

2 participants