A persistent collection library for Java 21+.
Persistent collections are immutable collections whose update operations return new collections that share storage with the original.
This library is an idiomatic Java port of Kotlin Immutable Collections by JetBrains.
import org.odenix.collections.*;
import static java.lang.System.out;
void main() {
// Create a persistent list
var list = PersistentList.of(1, 2, 3);
// Update operations return new collections
var updated = list.add(4);
// The original list is unchanged
out.println(list); // [1, 2, 3]
out.println(updated); // [1, 2, 3, 4]
// Use mutate() for efficient batched updates
var mutated = list.mutate(builder -> {
builder.add(4);
builder.add(5);
});
out.println(mutated); // [1, 2, 3, 4, 5]
// Maps preserve insertion order by default
var map = PersistentMap.of("a", 1, "b", 2);
var map2 = map.put("c", 3);
out.println(map2); // {a=1, b=2, c=3}
}Releases are available from Maven Central: org.odenix:odenix-collections
Gradle:
implementation("org.odenix:odenix-collections:{version}")Maven:
<dependency>
<groupId>org.odenix</groupId>
<artifactId>odenix-collections</artifactId>
<version>{version}</version>
</dependency>Mill:
mvn"org.odenix:odenix-collections:{version}"The Java ecosystem lacks a modern, full-featured, production-ready persistent collection library. This library aims to fill this gap by porting Kotlin Immutable Collections to Java.
Kotlin Immutable Collections is a strong baseline:
- Modern data structures with efficient small and large representations
- Hash-based and insertion-ordered sets/maps
- Thoroughly tested and benchmarked
- Proven in production despite its experimental status
This Java port adds:
- An idiomatic Java API
- No dependency on the Kotlin standard library
- Java nullness annotations via JSpecify (only dependency, can be excluded)
API reference: latest Javadoc
The public API is organized around immutable and persistent collection types.
Unlike Kotlin Immutable Collections, immutable interfaces extend only Iterable, not Collection, List, Set, or Map.
Provide read-only access:
| Interface | Bases |
|---|---|
ImmutableCollection |
Iterable |
ImmutableList |
ImmutableCollection |
ImmutableSet |
ImmutableCollection |
ImmutableMap |
— |
Extend immutable interfaces with update operations that return new collections:
| Interface | Bases |
|---|---|
PersistentCollection |
ImmutableCollection |
PersistentList |
PersistentCollection, ImmutableList |
PersistentSet |
PersistentCollection, ImmutableSet |
PersistentMap |
ImmutableMap |
Expose JDK mutable collection interfaces for efficient batched updates:
| Interface | Bases |
|---|---|
PersistentCollection.Builder |
Collection |
PersistentList.Builder |
PersistentCollection.Builder, List |
PersistentSet.Builder |
PersistentCollection.Builder, Set |
PersistentMap.Builder |
Map |
PersistentList.of()PersistentSet.of(),PersistentMap.of()to preserve insertion orderPersistentSet.hashOf(),PersistentMap.hashOf()when insertion order does not matter
ImmutableList.from(),ImmutableSet.from(),ImmutableMap.from()for read-only accessPersistentList.from(),PersistentSet.from(),PersistentMap.from()for persistent updatesPersistentSet.hashFrom(),PersistentMap.hashFrom()when insertion order does not matter
add()addAll()remove()removeAll()mutate()
Returned collections may share storage with the original.
stream()parallelStream()
To keep the result persistent, pass the stream to from():
var lengths = PersistentList.from(names.stream().map(String::length));Build, test, repository layout, and porting instructions are in DEVELOPMENT.md.