【问题标题】:Get a random item from list using kotlin streams使用 kotlin 流从列表中获取随机项
【发布时间】:2017-12-16 21:33:22
【问题描述】:

如何以简单明了的方式从列表中获取随机项。
例如:如果我想从这个列表中得到一个偶数随机数。

val list = listOf(1, 2, 3, 4, 5, 6, 7, 9).filter { it % 2 == 0 }

注意:
我知道在 java 中有一些类似的答案可以解决这个问题,但我认为我们可以在 kotlin 中有更简洁的方式。

【问题讨论】:

    标签: random kotlin


    【解决方案1】:

    Kotlin 1.3 及更高版本:

    Kotlin 1.3 现在可用于多平台随机数生成器! 你可以这样使用它:

    import kotlin.random.Random
    
    fun main() {
        println(Random.nextBoolean())
        println(Random.nextInt())
    }
    

    Try it online!

    或者在你的情况下

    fun main() {
        val list = (1..9).filter { it % 2 == 0 }
        println(list.random())
    }
    

    Try it online!


    科特林 1.2

    从 Kotlin 1.2 开始,我们有了 Iterable.shuffled()。此方法可以帮助您使用List.take() 来提取您想要的元素数量(本例中只有一个)。

    val list = (1..9).filter { it % 2 == 0 }
    return list.shuffled().take(1)[0]
    

    此方法不如 1.2 之前的优化,但它适用于多平台上下文。根据您的上下文使用您需要的那个。


    在 Kotlin 1.2 之前

    在 Kotlin 1.2 之前,多平台上下文中不存在生成随机数的解决方案。最简单的解决方案是直接调用 Platform Random。

    JVM

    在 JVM 上,如果我们使用 JDK > 1.6,我们使用 Random 甚至 ThreadLocalRandom

    import java.util.Random
    
    fun IntRange.random() = Random().nextInt((endInclusive + 1) - start) + start
    

    JS

    在 JS 上,我们使用Math.Random

    fun IntRange.random() = (Math.random() * ((endInclusive + 1) - start) + start).toInt()
    

    【讨论】:

    • 我正在使用这种方法,但我需要洗牌整个列表,这在外部效率低下。当您只想获得一个随机元素时。
    • 对所有列表项的随机迭代的实现。以及如何更灵活并在多平台环境下工作。
    • java.util.Random 在 Kotlin-js 或 Kotlin-native 上不可用。如果您不想重新实现自己的随机数生成器,这是唯一可能的方法。如果您需要从列表中随机选择 3 个值,shuffled().take() 可以做到,而不是您的方法(但它不是您的用例)。
    • 为什么不使用“list.shuffled()[0]”
    • 因为使用 take 可以提取 1 个或多个值。如果您只需要一个值,您的解决方案几乎相同。
    【解决方案2】:

    我认为最简单和最简洁的方法是创建返回随机元素的扩展函数,以便可以这样使用:

    val random = list.random()
    

    扩展功能:

    /**
     * Returns a random element.
     */
    fun <E> List<E>.random(): E? = if (size > 0) get(Random().nextInt(size)) else null
    

    感谢@Francesco 的评论,这里是另一个将 Random 实例作为随机源的函数

    /**
     * Returns a random element using the specified [random] instance as the source of randomness.
     */
    fun <E> List<E>.random(random: java.util.Random): E? = if (size > 0) get(random.nextInt(size)) else null
    

    【讨论】:

    • 谨防每次都使用新初始化的Random 实例。通常随机性需要几次调用才能调整到可接受的阈值。
    • 有趣,我可以想到这种方法的一个变体,它采用预先指定的随机数。我会添加它。感谢您的注意。
    • Random() 的当前实现是:public Random() { this(seedUniquifier() ^ System.nanoTime());} @FrancescoMenzani,您对新初始化的实例有什么顾虑?
    • 我更关心的是每次调用时的对象创建。另外,我建议将一个存储实例设为私有,以便它只能在声明它的文件中访问。
    【解决方案3】:

    为了得到一个随机数而对整个列表进行打乱是浪费性能。

    使用 random() 代替 Kotin 1.3 中引入的

    val randomNumber = listOf(1, 6, 8).random()
    

    对于您的特殊用例,首先过滤列表并从剩余项目中选择一个随机数是有意义的。

    val randomEvenNumber = listOf(1, 2, 3, 4, 5, 6, 7, 9).filter { it % 2 == 0 }.random()
    

    【讨论】:

      【解决方案4】:

      正如其他人所说,list.random() 现在应该可以解决问题,但如果您不知道列表是否为空,.random() 可能会引发异常。除了将.randomOrNull() 实现为扩展函数之外,您还可以使用list.shuffled().firstOrNull() 破解它,但要注意这对于长列表来说很慢。

      .randomOrNull() 方法:

      fun <E> List<E>.randomOrNull(): E? = if (size > 0) random() else null
      

      【讨论】:

        【解决方案5】:

        如果你想要一个列表中的 25 个随机元素,我会这样做:

        var suliTraffic = ArrayList<SuliTraffic>()
        var randomsuliTraffic = ArrayList<SuliTraffic>()
        
        // I have add some data to my Array List
        randomsuliTraffic.add(new)
        
        // Here i shuffle all the information i have wich is over 400 object
        randomsuliTraffic.shuffle()
        
        // now you can just simply get 25 item from this randomsuliTraffic like this
        for (i in randomsuliTraffic.take(25)){
            suliTraffic.add(i)
        } 
        

        现在您可以在suliTraffic 列表中找到 25 个随机元素。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-20
          • 1970-01-01
          • 2018-07-25
          • 2023-03-11
          • 2015-09-22
          • 1970-01-01
          相关资源
          最近更新 更多