From 7e4528e21782960b10578c530fdfddded6592b80 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 3 Jul 2018 03:45:09 +0300 Subject: [PATCH] Common shuffle/shuffled with the specified random source KT-17261, KT-9010 --- libraries/stdlib/js/src/kotlin/collections.kt | 13 ++-------- .../kotlin/collections/MutableCollections.kt | 25 +++++++++++++++++++ .../collections/MutableCollectionsTest.kt | 13 ++++++++++ .../kotlin-stdlib-runtime-merged.txt | 2 ++ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/libraries/stdlib/js/src/kotlin/collections.kt b/libraries/stdlib/js/src/kotlin/collections.kt index 40ab566f097..bfaed4a2194 100644 --- a/libraries/stdlib/js/src/kotlin/collections.kt +++ b/libraries/stdlib/js/src/kotlin/collections.kt @@ -6,7 +6,7 @@ package kotlin.collections import kotlin.comparisons.naturalOrder -import kotlin.math.floor +import kotlin.random.Random /** Returns the array if it's not `null`, or an empty array otherwise. */ @kotlin.internal.InlineOnly @@ -87,16 +87,7 @@ public actual fun MutableList.fill(value: T): Unit { * See: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm */ @SinceKotlin("1.2") -public actual fun MutableList.shuffle(): Unit { - for (i in lastIndex downTo 1) { - val j = rand(i + 1) - val copy = this[i] - this[i] = this[j] - this[j] = copy - } -} - -private fun rand(upperBound: Int) = floor(kotlin.js.Math.random() * upperBound).toInt() +public actual fun MutableList.shuffle(): Unit = shuffle(Random) /** * Returns a new list with the elements of this list randomly shuffled. diff --git a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt index a9cf4286853..18cbd777a3d 100644 --- a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt +++ b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt @@ -8,6 +8,8 @@ package kotlin.collections +import kotlin.random.Random + /** * Removes a single instance of the specified element from this * collection, if it is present. @@ -259,3 +261,26 @@ private fun MutableCollection<*>.retainNothing(): Boolean { clear() return result } + +/** + * Randomly shuffles elements in this mutable list using the specified [random] instance as the source of randomness. + * + * See: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm + */ +@SinceKotlin("1.3") +public fun MutableList.shuffle(random: Random): Unit { + for (i in lastIndex downTo 1) { + val j = random.nextInt(i + 1) + val copy = this[i] + this[i] = this[j] + this[j] = copy + } +} + +/** + * Returns a new list with the elements of this list randomly shuffled + * using the specified [random] instance as the source of randomness. + */ +@SinceKotlin("1.3") +public fun Iterable.shuffled(random: Random): List = toMutableList().apply { shuffle(random) } + diff --git a/libraries/stdlib/test/collections/MutableCollectionsTest.kt b/libraries/stdlib/test/collections/MutableCollectionsTest.kt index 8b153f15e4a..84e6e770e7d 100644 --- a/libraries/stdlib/test/collections/MutableCollectionsTest.kt +++ b/libraries/stdlib/test/collections/MutableCollectionsTest.kt @@ -5,6 +5,7 @@ package test.collections +import kotlin.random.Random import kotlin.test.* @@ -114,4 +115,16 @@ class MutableCollectionTest { assertEquals(list.size, shuffled.distinct().size) } + @Test fun shuffledPredictably() { + val list = List(10) { it } + val shuffled1 = list.shuffled(Random(1)) + val shuffled11 = list.shuffled(Random(1)) + + assertEquals(shuffled1, shuffled11) + assertEquals("[1, 4, 0, 6, 2, 8, 9, 7, 3, 5]", shuffled1.toString()) + + val shuffled2 = list.shuffled(Random(42)) + assertEquals("[5, 0, 4, 9, 2, 8, 1, 7, 6, 3]", shuffled2.toString()) + } + } diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 48ee34e5ff7..7fa8bafb5a9 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -2343,8 +2343,10 @@ public final class kotlin/collections/CollectionsKt { public static final fun retainAll (Ljava/util/List;Lkotlin/jvm/functions/Function1;)Z public static final fun reverse (Ljava/util/List;)V public static final fun reversed (Ljava/lang/Iterable;)Ljava/util/List; + public static final fun shuffle (Ljava/util/List;Lkotlin/random/Random;)V public static final fun shuffled (Ljava/lang/Iterable;)Ljava/util/List; public static final fun shuffled (Ljava/lang/Iterable;Ljava/util/Random;)Ljava/util/List; + public static final fun shuffled (Ljava/lang/Iterable;Lkotlin/random/Random;)Ljava/util/List; public static final fun single (Ljava/lang/Iterable;)Ljava/lang/Object; public static final fun single (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun single (Ljava/util/List;)Ljava/lang/Object;