Skip to content

Commit

Permalink
Add persistent Collection builder functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Goooler committed Jan 18, 2024
1 parent 89e63fb commit c90d590
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 0 deletions.
5 changes: 5 additions & 0 deletions core/api/kotlinx-collections-immutable.api
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
public final class kotlinx/collections/immutable/ExtensionsKt {
public static final fun buildPersistentHashMap (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentMap;
public static final fun buildPersistentHashSet (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentSet;
public static final fun buildPersistentList (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentList;
public static final fun buildPersistentMap (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentMap;
public static final fun buildPersistentSet (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentSet;
public static final fun immutableHashMapOf ([Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap;
public static final fun immutableHashSetOf ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet;
public static final fun immutableListOf ()Lkotlinx/collections/immutable/PersistentList;
Expand Down
58 changes: 58 additions & 0 deletions core/commonMain/src/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -766,3 +766,61 @@ public fun <K, V> Map<K, V>.toPersistentHashMap(): PersistentMap<K, V>
= this as? PersistentHashMap
?: (this as? PersistentHashMapBuilder<K, V>)?.build()
?: PersistentHashMap.emptyOf<K, V>().putAll(this)

/**
* Builds a new [PersistentList] by populating a [PersistentList.Builder] using the given [builderAction]
* and returning an immutable list with the same elements.
*
* The list passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside the function produces an unspecified behavior.
*/
public inline fun <T> buildPersistentList(builderAction: PersistentList.Builder<T>.() -> Unit): PersistentList<T> =
persistentListOf<T>().builder().apply(builderAction).build()

/**
* Builds a new [PersistentSet] by populating a [PersistentSet.Builder] using the given [builderAction]
* and returning an immutable set with the same elements.
*
* The set passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside the function produces an unspecified behavior.
*
* Elements of the set are iterated in the order they were added by the [builderAction].
*/
public inline fun <T> buildPersistentSet(builderAction: PersistentSet.Builder<T>.() -> Unit): PersistentSet<T> =
persistentSetOf<T>().builder().apply(builderAction).build()

/**
* Builds a new [PersistentSet] by populating a [PersistentSet.Builder] using the given [builderAction]
* and returning an immutable set with the same elements.
*
* The set passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside the function produces an unspecified behavior.
*
* Order of the elements in the returned set is unspecified.
*/
public inline fun <T> buildPersistentHashSet(builderAction: PersistentSet.Builder<T>.() -> Unit): PersistentSet<T> =
persistentHashSetOf<T>().builder().apply(builderAction).build()

/**
* Builds a new [PersistentMap] by populating a [PersistentMap.Builder] using the given [builderAction]
* and returning an immutable map with the same key-value pairs.
*
* The map passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside the function produces an unspecified behavior.
*
* Entries of the map are iterated in the order they were added by the [builderAction].
*/
public inline fun <K, V> buildPersistentMap(builderAction: PersistentMap.Builder<K, V>.() -> Unit): PersistentMap<K, V> =
persistentMapOf<K, V>().builder().apply(builderAction).build()

/**
* Builds a new [PersistentMap] by populating a [PersistentMap.Builder] using the given [builderAction]
* and returning an immutable map with the same key-value pairs.
*
* The map passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside the function produces an unspecified behavior.
*
* Order of the entries in the returned map is unspecified.
*/
public inline fun <K, V> buildPersistentHashMap(builderAction: PersistentMap.Builder<K, V>.() -> Unit): PersistentMap<K, V> =
persistentHashMapOf<K, V>().builder().apply(builderAction).build()
10 changes: 10 additions & 0 deletions core/commonTest/src/contract/list/ImmutableListTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ class ImmutableListTest {
}
}

@Test fun buildPersistentList() {
val expected = persistentListOf(1, 2, 3, 4, 5, 6)
val actual = buildPersistentList {
for (i in 1..6) {
add(i)
}
}
assertEquals(expected, actual)
}

@Test fun subListOfBuilder() {
val list = "abcxaxyz12".toImmutableList().toPersistentList()
val builder = list.builder()
Expand Down
30 changes: 30 additions & 0 deletions core/commonTest/src/contract/map/ImmutableMapTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ class ImmutableHashMapTest : ImmutableMapTest() {
compareMaps(expected, builder1.build())
}

@Test fun buildPersistentHashMap() {
val expected = mutableMapOf<String, Int>()
val actual = buildPersistentHashMap {
for (i in 300..400) {
put("$i", i)
expected["$i"] = i
}
for (i in 0..200) {
put("$i", i)
expected["$i"] = i
}
}
compareMapsUnordered(expected, actual)
}

@Test fun regressionGithubIssue109() {
// https://github.com/Kotlin/kotlinx.collections.immutable/issues/109
val map0 = immutableMapOf<Int, Int>().put(0, 0).put(1, 1).put(32, 32)
Expand Down Expand Up @@ -139,6 +154,21 @@ class ImmutableOrderedMapTest : ImmutableMapTest() {
changing.add("break iteration")
assertFailsWith<ConcurrentModificationException> { builder.filter { it.key === changing } }
}

@Test fun buildPersistentMap() {
val expected = mutableMapOf<String, Int>()
val actual = buildPersistentMap {
for (i in 300..400) {
put("$i", i)
expected["$i"] = i
}
for (i in 0..200) {
put("$i", i)
expected["$i"] = i
}
}
compareMaps(expected, actual)
}
}

abstract class ImmutableMapTest {
Expand Down
22 changes: 22 additions & 0 deletions core/commonTest/src/contract/set/ImmutableSetTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,17 @@ class ImmutableHashSetTest : ImmutableSetTestBase() {
compareSets(immutableSetOf<Int>(), left - right)
}
}

@Test fun buildPersistentHashSet() {
val expected = mutableSetOf<Int>()
val actual = buildPersistentHashSet {
for (i in 0..2000) {
add(i)
expected.add(i)
}
}
compareSetsUnordered(expected, actual)
}
}

class ImmutableOrderedSetTest : ImmutableSetTestBase() {
Expand All @@ -221,6 +232,17 @@ class ImmutableOrderedSetTest : ImmutableSetTestBase() {
changing.add("break iteration")
assertFailsWith<ConcurrentModificationException> { builder.filter { it === changing } }
}

@Test fun buildPersistentSet() {
val expected = mutableSetOf<Int>()
val actual = buildPersistentSet {
for (i in 0..2000) {
add(i)
expected.add(i)
}
}
compareSets(expected, actual)
}
}

abstract class ImmutableSetTestBase {
Expand Down

0 comments on commit c90d590

Please sign in to comment.