tutorial español ejemplos compiler kotlin

español - kotlin vs java



Kotlin: Convierte una lista grande a una lista secundaria del tamaño de partición establecido (5)

Con Kotlin 1.2-M1, de acuerdo con sus necesidades, puede elegir una de las siguientes formas para resolver su problema.

# 1. Usando chunked(size: Int)

fun main(args: Array<String>) { val list = listOf(2, 4, 3, 10, 8, 7) val newList = list.chunked(2) //val newList = list.chunked(size = 2) // also works print(newList) } /* prints: [[2, 4], [3, 10], [8, 7], [9]] */

# 2. Usando windowed(size: Int, step: Int)

fun main(args: Array<String>) { val list = listOf(2, 4, 3, 10, 8, 7, 9) val newList = list.windowed(2, 2) //val newList = list.windowed(size = 2, step = 2) // also works println(newList) } /* prints: [[2, 4], [3, 10], [8, 7], [9]] */

Estoy buscando una función equivalente a la clasificación de Groovy que dividiría una lista grande en lotes para su procesamiento. Sí vi una subList que podía adaptarse a una función similar, pero quería comprobar y asegurarme de que no me estaba perdiendo una alternativa simple o loca para rodar la mía.


En Kotlin 1.2 M2 y chunked posteriores, puede usar chunked y windowed (ver Kotlin 1.2 M2 está publicado | Blog de Kotlin ). Tenga en cuenta que también hay variaciones de Sequence (consulte kotlin.sequences - Lenguaje de programación Kotlin ).

Para las versiones de Kotlin anteriores a 1.2 M2, recomiendo utilizar Lists.partition (List, int) de google-guava (usa java.util.List.subList(int, int) ):

Si no está familiarizado con la Guava consulte la Wiki de CollectionUtilitiesExplained · google / guava para obtener más detalles.

Puede crear su propia función de extensión Kotlin para ella si desea:

fun <T> List<T>.collate(size: Int): List<List<T>> = Lists.partition(this, size)

Si desea una función de extensión para listas mutables, en un archivo Kotlin separado (para evitar conflictos de declaración de plataforma):

fun <T> MutableList<T>.collate(size: Int): List<MutableList<T>> = Lists.partition(this, size)

Si quieres algo cargado como en la respuesta de Jayson Minard puedes usar Iterables.partition (Iterable, int) . También puede interesarle Iterables.paddedPartition (Iterable, int) si desea rellenar la última lista secundaria si es más pequeña que el size especificado. Éstos devuelven Iterable<List<T>> (No veo mucho sentido hacerlo Iterable<Iterable<T>> ya que la lista subList devuelve una vista eficiente).

Si, por alguna razón, no quiere depender de la guayaba, puede rodar la suya fácilmente con la función de subList que mencionó:

fun <T> List<T>.collate(size: Int): List<List<T>> { require(size > 0) return if (isEmpty()) { emptyList() } else { (0..lastIndex / size).map { val fromIndex = it * size val toIndex = Math.min(fromIndex + size, this.size) subList(fromIndex, toIndex) } } }

o

fun <T> List<T>.collate(size: Int): Sequence<List<T>> { require(size > 0) return if (isEmpty()) { emptySequence() } else { (0..lastIndex / size).asSequence().map { val fromIndex = it * size val toIndex = Math.min(fromIndex + size, this.size) subList(fromIndex, toIndex) } } }


Lamentablemente, no hay una función incorporada para eso todavía y si bien las implementaciones funcionales y basadas en Sequence de otras respuestas se ven bien, si solo necesitas la List de List , te sugiero que escribas un poco de feo, imperativo, pero eficaz código.

Este es mi resultado final:

fun <T> List<T>.batch(chunkSize: Int): List<List<T>> { if (chunkSize <= 0) { throw IllegalArgumentException("chunkSize must be greater than 0") } val capacity = (this.size + chunkSize - 1) / chunkSize val list = ArrayList<ArrayList<T>>(capacity) for (i in 0 until this.size) { if (i % chunkSize == 0) { list.add(ArrayList(chunkSize)) } list.last().add(this.get(i)) } return list }


Una solución más simplista / funcional sería

val items = (1..100).map { "foo_${it}" } fun <T> Iterable<T>.batch(chunkSize: Int) = withIndex(). // create index value pairs groupBy { it.index / chunkSize }. // create grouping index map { it.value.map { it.value } } // split into different partitions items.batch(3)

Nota 1: Personalmente preferiría la partition como un nombre de método aquí, pero ya está presente en la stdlib de Kotlin para separar las listas en 2 partes dado un predicado.

Nota 2: la solución de iterador de Jayson puede escalar mejor que esta solución para colecciones grandes.


NOTA: Para Kotlin 1.2 y más reciente, vea las funciones chunked y con windowed que ahora están en la biblioteca estándar. No hay necesidad de una solución personalizada.

Aquí hay una implementación de una función de extensión por lotes lenta que tomará una colección, o cualquier cosa que pueda convertirse en una Sequence y devolver una Sequence de List cada tamaño, con la última de ese tamaño o más pequeña.

Ejemplo de uso para iterar una lista como lotes:

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

Ejemplo para convertir lotes de List a Set :

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

Vea el primer caso de prueba a continuación para mostrar la salida específica dada entrada.

Código para la función 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() } } }

Pruebas unitarias que prueban que funciona:

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) } } assertEquals(testList, itemsSeen) assertEquals(groupSeenCount, expectedGroups) } @Test fun groupsOfExactSize() { testStream(listOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), 5, 3) } @Test fun groupsOfOddSize() { testStream(listOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18), 5, 4) testStream(listOf(1,2,3,4), 3, 2) } @Test fun groupsOfLessThanBatchSize() { testStream(listOf(1,2,3), 5, 1) testStream(listOf(1), 5, 1) } @Test fun groupsOfSize1() { testStream(listOf(1,2,3), 1, 3) } @Test fun groupsOfSize0() { val testList = listOf(1,2,3) val groupCountZero = testList.asSequence().batch(0).toList().size assertEquals(0, groupCountZero) val groupCountNeg = testList.asSequence().batch(-1).toList().size assertEquals(0, groupCountNeg) } @Test fun emptySource() { listOf<Int>().asSequence().batch(1).forEach { groupStream -> fail() } } }