una recorrer que por objetos mutables listas lista inmutables inmutable inmutabilidad crear colecciones clases kotlin

recorrer - Kotlin y colecciones inmutables?



por que string es inmutable (5)

Como puede ver en otras respuestas, Kotlin tiene interfaces de solo lectura para colecciones mutables que le permiten ver una colección a través de una lente de solo lectura. Pero la colección se puede omitir a través de la transmisión o manipular desde Java. Pero en el código cooperativo de Kotlin que está bien, la mayoría de los usos no necesitan colecciones verdaderamente inmutables y si su equipo evita la conversión a la forma mutable de la colección, entonces tal vez no necesite colecciones completamente inmutables.

Las colecciones de Kotlin permiten mutaciones de copia en cambio, así como mutaciones perezosas. Entonces, para responder parte de sus preguntas, cosas como filter , map , map flatmap , operadores + - todos crean copias cuando se usan contra colecciones no perezosas. Cuando se usan en una Sequence , modifican los valores como la colección a medida que se accede y continúan siendo perezosos (lo que resulta en otra Sequence ). Aunque para una Sequence , llamar a algo como toList , toSet , toMap dará como resultado la copia final. Al nombrar la convención, casi todo lo que comienza con es hacer una copia.

En otras palabras, la mayoría de los operadores le devuelven el mismo tipo con el que comenzó, y si ese tipo es "de solo lectura", recibirá una copia. Si ese tipo es perezoso, aplicará perezosamente el cambio hasta que exija la colección en su totalidad.

Algunas personas los quieren por otros motivos, como el procesamiento paralelo. En esos casos, podría ser mejor mirar colecciones de alto rendimiento diseñadas solo para esos fines. Y solo úselos en esos casos, no en todos los casos generales.

En el mundo de JVM, es difícil evitar interoperabilidad con bibliotecas que desean colecciones estándar de Java, y la conversión a / de estas colecciones agrega una gran carga y sobrecarga para las bibliotecas que no admiten las interfaces comunes. Kotlin ofrece una buena combinación de interoperabilidad y falta de conversión, con protección de solo lectura por contrato.

Entonces, si no puede evitar querer colecciones inmutables, Kotlin trabaja fácilmente con cualquier cosa desde el espacio JVM:

Además, el equipo de Kotlin está trabajando en colecciones inmutables de forma nativa para Kotlin, ese esfuerzo se puede ver aquí: https://github.com/Kotlin/kotlinx.collections.immutable

Existen muchos otros marcos de recopilación para todas las necesidades y limitaciones diferentes, Google es tu amigo para encontrarlos. No hay razón para que el equipo de Kotlin necesite reinventarlos para su biblioteca estándar. Tiene muchas opciones, y se especializan en diferentes cosas como rendimiento, uso de memoria, no boxeo, inmutabilidad, etc. "La elección es buena" ... por lo tanto, otras: HPCC , HPCC-RT , FastUtil , Koloboke , Trove y más ...

Incluso hay esfuerzos como Pure4J que, dado que Kotlin admite el procesamiento de Anotación ahora, tal vez pueda tener un puerto a Kotlin para ideales similares.

Estoy aprendiendo Kotlin y parece probable que quiera usarlo como mi idioma principal el próximo año. Sin embargo, sigo recibiendo investigaciones contradictorias de que Kotlin tiene o no colecciones inmutables y estoy tratando de averiguar si necesito usar Google Guava.

¿Puede alguien darme alguna orientación sobre esto? ¿Utiliza por defecto colecciones inmutables? ¿Qué operadores devuelven colecciones mutables o inmutables? Si no, ¿hay planes para implementarlos?


Es confuso pero hay tres tipos de inmutabilidad, no dos:

  1. Mutable: se supone que debes cambiar la colección ( MutableList de Kotlin)
  2. Solo lectura: se supone que NO debes cambiarlo ( List de Kotlin) pero algo puede (emitirse a Mutable o cambiar de Java)
  3. Inmutable: nadie puede cambiarlo (las colecciones inmutables de Guavas)

Entonces, en el caso (2) List es solo una interfaz que no tiene métodos de mutación, pero puede cambiar la instancia si la MutableList en MutableList .

Con Guava (caso (3)) está a salvo de que cualquiera cambie la colección, incluso con un yeso o de otro hilo.

Kotlin eligió ser de solo lectura para usar colecciones Java directamente, por lo que no hay gastos generales o conversión al usar colecciones Java.


Kotlin 1.0 no tendrá colecciones inmutables en la biblioteca estándar. Sin embargo, tiene interfaces de solo lectura y mutables. Y nada le impide utilizar bibliotecas de colecciones inmutables de terceros.

Los métodos en la interfaz de Kotlin''s List "admiten solo acceso de solo lectura a la lista", mientras que los métodos en su interfaz MutableList admiten "agregar y eliminar elementos". Sin embargo, ambos son solo interfaces .

La interfaz de la List de Kotlin impone el acceso de solo lectura en tiempo de compilación en lugar de diferir tales comprobaciones en tiempo de ejecución como java.util.Collections.unmodifiableList(java.util.List) (que "devuelve una vista no modificable de la lista especificada ... [donde] intenta modificar la lista devuelta ... da como resultado una UnsupportedOperationException ". No impone la inmutabilidad.

Considere el siguiente código de Kotlin:

import com.google.common.collect.ImmutableList import kotlin.test.assertEquals import kotlin.test.assertFailsWith fun main(args: Array<String>) { val readOnlyList: List<Int> = arrayListOf(1, 2, 3) val mutableList: MutableList<Int> = readOnlyList as MutableList<Int> val immutableList: ImmutableList<Int> = ImmutableList.copyOf(readOnlyList) assertEquals(readOnlyList, mutableList) assertEquals(mutableList, immutableList) // readOnlyList.add(4) // Kotlin: Unresolved reference: add mutableList.add(4) assertFailsWith(UnsupportedOperationException::class) { immutableList.add(4) } assertEquals(readOnlyList, mutableList) assertEquals(mutableList, immutableList) }

Observe cómo readOnlyList es una List y los métodos como add no se pueden resolver (y no se compilarán), mutableList se puede mutar de forma natural y add on immutableList (de Google Guava) también se pueden resolver en tiempo de compilación, pero arroja una excepción en tiempo de ejecución

Todas las aserciones anteriores pasan con excepción de la última que da como resultado la Exception in thread "main" java.lang.AssertionError: Expected <[1, 2, 3, 4]>, actual <[1, 2, 3]>. es decir, mutamos con éxito una List solo lectura.

Tenga en cuenta que el uso de listOf(...) lugar de arrayListOf(...) devuelve una lista efectivamente inmutable, ya que no puede convertirla en ningún tipo de lista mutable. Sin embargo, el uso de la interfaz de List para una variable no impide que se le MutableList una MutableList ( MutableList<E> extiende la List<E> ).

Finalmente, tenga en cuenta que una interfaz en Kotlin (así como en Java) no puede imponer la inmutabilidad ya que "no puede almacenar el estado" (ver Interfaces ). Como tal, si desea una colección inmutable, debe usar algo como los proporcionados por Google Guava.

Ver también ImmutableCollectionsExplained · Wiki de google / guava · GitHub


La List de Kotlin de la biblioteca estándar es de solo lectura:

interface List<out E> : Collection<E> (source)

Una colección genérica ordenada de elementos. Los métodos en esta interfaz solo admiten acceso de solo lectura a la lista; El acceso de lectura / escritura es compatible a través de la interfaz MutableList.

Parámetros
E - el tipo de elementos contenidos en la lista.

Como se mencionó, también está la MutableList

interface MutableList<E> : List<E>, MutableCollection<E> (source)

Una colección genérica ordenada de elementos que admite agregar y eliminar elementos.

Parámetros
E - el tipo de elementos contenidos en la lista.

Debido a esto, Kotlin impone un comportamiento de solo lectura a través de sus interfaces, en lugar de lanzar Excepciones en tiempo de ejecución como lo hacen las implementaciones Java predeterminadas.

Del mismo modo, hay un MutableCollection , MutableIterable , MutableIterator , MutableListIterator , MutableMap y MutableSet , consulte la documentación de stdlib .


NOTA: Esta respuesta está aquí porque el código es simple y de código abierto y puede usar esta idea para hacer que sus colecciones que cree sean inmutables. No pretende ser solo un anuncio de la biblioteca.

En la biblioteca de Klutter , hay nuevos envoltorios inmutables de Kotlin que usan la delegación de Kotlin para envolver una interfaz de colección Kotlin existente con una capa protectora sin ningún impacto en el rendimiento. Entonces no hay forma de convertir la colección, su iterador u otras colecciones que podría devolver a algo que podría modificarse. Se vuelven en efecto inmutables.

Lanzado Klutter 1.20.0 que agrega protectores inmutables para colecciones existentes, basado en una respuesta SO de @miensol, proporciona un delegado liviano alrededor de las colecciones que evita cualquier vía de modificación, incluida la conversión a un tipo mutable y luego la modificación. Y Klutter va un paso más allá al proteger las subcolecciones como iterator, listIterator, entrySet, etc. Todas esas puertas están cerradas y utilizan la delegación de Kotlin para la mayoría de los métodos que no afecta el rendimiento. Simplemente llame a myCollection.asReadonly() ( protect ) o myCollection.toImmutable() ( copie y luego proteja ) y el resultado es la misma interfaz pero protegida.

Aquí hay un ejemplo del código que muestra cuán simple es la técnica, básicamente delegando la interfaz a la clase real mientras se anulan los métodos de mutación y cualquier subcolección devuelta se envuelve sobre la marcha.

/** * Wraps a List with a lightweight delegating class that prevents casting back to mutable type */ open class ReadOnlyList <T>(protected val delegate: List<T>) : List<T> by delegate, ReadOnly, Serializable { companion object { @JvmField val serialVersionUID = 1L } override fun iterator(): Iterator<T> { return delegate.iterator().asReadOnly() } override fun listIterator(): ListIterator<T> { return delegate.listIterator().asReadOnly() } override fun listIterator(index: Int): ListIterator<T> { return delegate.listIterator(index).asReadOnly() } override fun subList(fromIndex: Int, toIndex: Int): List<T> { return delegate.subList(fromIndex, toIndex).asReadOnly() } override fun toString(): String { return "ReadOnly: ${super.toString()}" } override fun equals(other: Any?): Boolean { return delegate.equals(other) } override fun hashCode(): Int { return delegate.hashCode() } }

Junto con las funciones de extensión de ayuda para facilitar el acceso:

/** * Wraps the List with a lightweight delegating class that prevents casting back to mutable type, * specializing for the case of the RandomAccess marker interface being retained if it was there originally */ fun <T> List<T>.asReadOnly(): List<T> { return this.whenNotAlreadyReadOnly { when (it) { is RandomAccess -> ReadOnlyRandomAccessList(it) else -> ReadOnlyList(it) } } } /** * Copies the List and then wraps with a lightweight delegating class that prevents casting back to mutable type, * specializing for the case of the RandomAccess marker interface being retained if it was there originally */ @Suppress("UNCHECKED_CAST") fun <T> List<T>.toImmutable(): List<T> { val copy = when (this) { is RandomAccess -> ArrayList<T>(this) else -> this.toList() } return when (copy) { is RandomAccess -> ReadOnlyRandomAccessList(copy) else -> ReadOnlyList(copy) } }

Puede ver la idea y extrapolar para crear las clases que faltan a partir de este código que repite los patrones para otros tipos referenciados. O vea el código completo aquí:

https://github.com/kohesive/klutter/blob/master/core-jdk6/src/main/kotlin/uy/klutter/core/common/Immutable.kt

Y con las pruebas que muestran algunos de los trucos que permitieron modificaciones antes, pero que ahora no lo hacen, junto con los lanzamientos y llamadas bloqueados con estos envoltorios.

https://github.com/kohesive/klutter/blob/master/core-jdk6/src/test/kotlin/uy/klutter/core/collections/TestImmutable.kt