Add `toList()` and `toMap()` to the respective snapshot collections
Relnote: """`SnapshotStateList` and `SnapshotStateMap` now have
explicit implementaions of `toList()` and `toMap()`, respectfully.
These methods returns their current content without peforming a
copy as they return the internal immutable data used to store their
content. This value can be used, for example, to produce a flow
of values using `snapshotFlow` without requiring copying of the data."""
Test: ./gradlew :composer:r:r:tDUT
Fixes: 245791884
Change-Id: Ica2bd64be73862b8497bf756c5b0537987d691e5
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 8b0cc2a..f4bfd21 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -907,6 +907,7 @@
method public boolean retainAll(java.util.Collection<E!> elements);
method public T! set(int index, T? element);
method public java.util.List<T> subList(int fromIndex, int toIndex);
+ method public java.util.List<T> toList();
property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
property public int size;
}
@@ -930,6 +931,7 @@
method public V? put(K? key, V? value);
method public void putAll(java.util.Map<? extends K,? extends V> from);
method public V? remove(Object? key);
+ method public java.util.Map<K,V> toMap();
property public java.util.Set<java.util.Map.Entry<K,V>> entries;
property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
property public java.util.Set<K> keys;
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index be29da0..6dca9cb 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -983,6 +983,7 @@
method public boolean retainAll(java.util.Collection<E!> elements);
method public T! set(int index, T? element);
method public java.util.List<T> subList(int fromIndex, int toIndex);
+ method public java.util.List<T> toList();
property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
property public int size;
}
@@ -1006,6 +1007,7 @@
method public V? put(K? key, V? value);
method public void putAll(java.util.Map<? extends K,? extends V> from);
method public V? remove(Object? key);
+ method public java.util.Map<K,V> toMap();
property public java.util.Set<java.util.Map.Entry<K,V>> entries;
property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
property public java.util.Set<K> keys;
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 684ac84..d9a5576 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -947,6 +947,7 @@
method public boolean retainAll(java.util.Collection<E!> elements);
method public T! set(int index, T? element);
method public java.util.List<T> subList(int fromIndex, int toIndex);
+ method public java.util.List<T> toList();
property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
property public int size;
}
@@ -970,6 +971,7 @@
method public V? put(K? key, V? value);
method public void putAll(java.util.Map<? extends K,? extends V> from);
method public V? remove(Object? key);
+ method public java.util.Map<K,V> toMap();
property public java.util.Set<java.util.Map.Entry<K,V>> entries;
property public androidx.compose.runtime.snapshots.StateRecord firstStateRecord;
property public java.util.Set<K> keys;
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
index 12d0ca2..cf562db 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
@@ -42,6 +42,23 @@
firstStateRecord = value as StateListStateRecord<T>
}
+ /**
+ * Return a list containing all the elements of this list.
+ *
+ * The list returned is immutable and returned will not change even if the content of the list
+ * is changed in the same snapshot. It also will be the same instance until the content is
+ * changed. It is not, however, guaranteed to be the same instance for the same list as adding
+ * and removing the same item from the this list might produce a different instance with the
+ * same content.
+ *
+ * This operation is O(1) and does not involve a physically copying the list. It instead
+ * returns the underlying immutable list used internally to store the content of the list.
+ *
+ * It is recommended to use [toList] when using returning the value of this list from
+ * [androidx.compose.runtime.snapshotFlow].
+ */
+ fun toList(): List<T> = readable.list
+
internal val modification: Int get() = withCurrent { modification }
@Suppress("UNCHECKED_CAST")
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
index 2b14ca4..aceccb7 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
@@ -41,6 +41,22 @@
firstStateRecord = value as StateMapStateRecord<K, V>
}
+ /**
+ * Returns an immutable map containing all key-value pairs from the original map.
+ *
+ * The content of the map returned will not change even if the content of the map is changed in
+ * the same snapshot. It also will be the same instance until the content is changed. It is not,
+ * however, guaranteed to be the same instance for the same content as adding and removing the
+ * same item from the this map might produce a different instance with the same content.
+ *
+ * This operation is O(1) and does not involve a physically copying the map. It instead
+ * returns the underlying immutable map used internally to store the content of the map.
+ *
+ * It is recommended to use [toMap] when using returning the value of this map from
+ * [androidx.compose.runtime.snapshotFlow].
+ */
+ fun toMap(): Map<K, V> = readable.map
+
override val size get() = readable.map.size
override fun containsKey(key: K) = readable.map.containsKey(key)
override fun containsValue(value: V) = readable.map.containsValue(value)
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
index b3cb799..d03fa3f 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
@@ -600,6 +600,25 @@
}
}
+ @Test
+ fun currentValueOfTheList() {
+ val list = mutableStateListOf<Int>()
+ val lists = mutableListOf<List<Int>>()
+ repeat(100) {
+ Snapshot.withMutableSnapshot {
+ list.add(it)
+ lists.add(list.toList())
+ }
+ }
+ repeat(100) { index ->
+ val current = lists[index]
+ assertEquals(index + 1, current.size)
+ current.forEachIndexed { i, value ->
+ assertEquals(i, value)
+ }
+ }
+ }
+
private fun <T> validate(list: MutableList<T>, block: (list: MutableList<T>) -> Unit) {
val normalList = list.toMutableList()
block(normalList)
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
index 5e51bb1..70d93d7 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
@@ -494,6 +494,25 @@
}
@Test
+ fun currentValueOfTheMap() {
+ val map = mutableStateMapOf<Int, String>()
+ val maps = mutableListOf<Map<Int, String>>()
+ repeat(100) {
+ Snapshot.withMutableSnapshot {
+ map[it] = it.toString()
+ maps.add(map.toMap())
+ }
+ }
+ repeat(100) { index ->
+ val current = maps[index]
+ assertEquals(index + 1, current.size)
+ repeat(index) {
+ assertEquals(current[it], it.toString())
+ }
+ }
+ }
+
+ @Test
@IgnoreJsTarget
@OptIn(ExperimentalCoroutinesApi::class)
fun concurrentModificationInGlobal_put_new() = runTest {