小编典典

Kotlin:将大列表转换为设置分区大小的子列表

Kotlin

我正在寻找一个与Groovy 的 collat​​e等效的函数,它将一个大的 List 分成批处理进行处理。我确实看到subList了哪些可以适应类似的功能,但想检查并确保我没有错过一个内置的或疯狂的简单替代方法来滚动我自己的。


阅读 305

收藏
2022-06-27

共1个答案

小编典典

注意: 对于 Kotlin 1.2 和更新版本,请参阅标准库中的chunkedwindowed函数。无需定制解决方案。


这是一个惰性批处理扩展函数的实现,它将接受一个集合,或者任何可以成为 aSequence并返回每个大小的 aSequence的东西,List最后一个是那个大小或更小。

将列表迭代为批次的示例用法:

myList.asSequence().batch(5).forEach { group ->
   // receive a Sequence of size 5 (or less for final)
}

将批次转换为的List示例Set

myList.asSequence().batch(5).map { it.toSet() }

请参阅下面的第一个测试用例,以显示给定特定输入的输出。

函数代码Sequence<T>.batch(groupSize)

public fun <T> Sequence<T>.batch(n: Int): Sequence<List<T>> {
    return BatchingSequence(this, n)
}

private class BatchingSequence<T>(val source: Sequence<T>, val batchSize: Int) : Sequence<List<T>> {
    override fun iterator(): Iterator<List<T>> = object : AbstractIterator<List<T>>() {
        val iterate = if (batchSize > 0) source.iterator() else emptyList<T>().iterator()
        override fun computeNext() {
            if (iterate.hasNext()) setNext(iterate.asSequence().take(batchSize).toList())
            else done() 
        }
    }
}

单元测试证明它有效:

class TestGroupingStream {

    @Test fun testConvertToListOfGroupsWithoutConsumingGroup() {
        val listOfGroups = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).asSequence().batch(2).toList()
        assertEquals(5, listOfGroups.size)
        assertEquals(listOf(1,2), listOfGroups[0].toList())
        assertEquals(listOf(3,4), listOfGroups[1].toList())
        assertEquals(listOf(5,6), listOfGroups[2].toList())
        assertEquals(listOf(7,8), listOfGroups[3].toList())
        assertEquals(listOf(9,10), listOfGroups[4].toList())
    }

    @Test fun testSpecificCase() {
        val originalStream = listOf(1,2,3,4,5,6,7,8,9,10)

        val results = originalStream.asSequence().batch(3).map { group ->
            group.toList()
        }.toList()

        assertEquals(listOf(1,2,3), results[0])
        assertEquals(listOf(4,5,6), results[1])
        assertEquals(listOf(7,8,9), results[2])
        assertEquals(listOf(10), results[3])
    }


    fun testStream(testList: List<Int>, batchSize: Int, expectedGroups: Int) {
        var groupSeenCount = 0
        var itemsSeen = ArrayList<Int>()

        testList.asSequence().batch(batchSize).forEach { groupStream ->
            groupSeenCount++
            groupStream.forEach { item ->
                itemsSeen.add(item)
            }
2022-06-27