Разница между типами List и Array в Котлине

В чем разница между List а также Array типы?
Кажется, можно выполнять с ними одни и те же операции (циклы, выражение фильтра и т. Д.), Есть ли разница в поведении или использовании?

val names1 = listOf("Joe","Ben","Thomas")
val names2 = arrayOf("Joe","Ben","Thomas")

for (name in names1)
    println(name)
for (name in names2)
    println(name)

5 ответов

Решение

Массивы и списки (представлены List<T> и его подтип MutableList<T>) есть много различий, вот наиболее значимые из них:

  • Array<T> это класс с известной реализацией: это последовательная область памяти фиксированного размера, хранящая элементы (а в JVM она представлена массивом Java).

    List<T> а также MutableList<T> это интерфейсы, которые имеют разные реализации: ArrayList<T>, LinkedList<T> и т.д. Представление памяти и логика операций списков определяются в конкретной реализации, например, индексация в LinkedList<T> идет по ссылкам и занимает O(N) время, тогда как ArrayList<T> хранит свои элементы в динамически распределенном массиве.

    val list1: List<Int> = LinkedList<Int>()
    val list2: List<Int> = ArrayList<Int>()
    
  • Array<T> является изменчивым (его можно изменить с помощью любой ссылки на него), но List<T> не имеет модифицирующих методов (это либо вид только для чтения MutableList<T> или реализация неизменяемого списка).

    val a = arrayOf(1, 2, 3)
    a[0] = a[1] // OK
    
    val l = listOf(1, 2, 3)
    l[0] = l[1] // doesn't compile
    
    val m = mutableListOf(1, 2, 3)
    m[0] = m[1] // OK
    
  • Массивы имеют фиксированный размер и не могут расширять или уменьшать сохраняющуюся идентичность (вам нужно скопировать массив, чтобы изменить его размер). Что касается списков, MutableList<T> имеет add а также remove функции, так что он может увеличивать и уменьшать свои размеры.

    val a = arrayOf(1, 2, 3)
    println(a.size) // will always be 3 for this array
    
    val l = mutableListOf(1, 2, 3)
    l.add(4)
    println(l.size) // 4
    
  • Array<T> инвариантен относительно T (Array<Int> не является Array<Number>), то же самое для MutableList<T>, но List<T> является ковариантным (List<Int> является List<Number>).

    val a: Array<Number> = Array<Int>(0) { 0 } // won't compile
    val l: List<Number> = listOf(1, 2, 3) // OK
    
  • Массивы оптимизированы для примитивов: есть отдельные IntArray, DoubleArray, CharArray и т. д., которые отображаются в примитивные массивы Java (int[], double[], char[]), а не в штучной упаковке (Array<Int> сопоставлен с Java Integer[]). Как правило, списки не имеют реализаций, оптимизированных для примитивов, хотя некоторые библиотеки (за пределами JDK) предоставляют списки, оптимизированные для примитивов.

  • List<T> а также MutableList<T> являются сопоставленными типами и имеют особое поведение в совместимости Java (Java's List<T> видно из Котлина как List<T> или же MutableList<T>). Массивы также отображаются, но у них есть другие правила взаимодействия Java.

  • Определенные типы массивов используются в аннотациях (примитивные массивы, Array<String> и массивы с enum class записи), и есть специальный синтаксис литерала массива для аннотаций. Списки и другие коллекции не могут быть использованы в аннотациях.

  • Что касается использования, то хорошей практикой является предпочтение использования списков, а не массивов везде, кроме критически важных для вашего кода частей, причина которых та же, что и для Java.

Основное отличие от использования состоит в том, что массивы имеют фиксированный размер, а (Mutable)List можно настроить их размер динамически. более того Array изменчиво, тогда как List не является.

более того kotlin.collections.List это интерфейс, реализованный среди других java.util.ArrayList, Это также продлено kotlin.collections.MutableList используется, когда необходимы коллекции, которые позволяют модифицировать элемент.

На уровне jvm Array представлен массивами. List с другой стороны, представлен java.util.List поскольку в Java нет неизменных эквивалентов коллекций.

В дополнение к вышесказанному, сравнение идентичности также отличается:

val l1 = listOf("a")
val l2 = listOf("a")
var x = (l1 == l2) // => true

val a1 = arrayOf("a")
val a2 = arrayOf("a")
var y = (a1 == a2) // => false

Объяснение со сложным объектом

      // lets define a data class
data class Color(var name : String)
      // lets define all 3 - array, list and mutableList for this Color object

val array = arrayOf(Color("Red"))
val list = listOf(Color("Red"))
val mutableList = mutableListOf(Color("Red"))
  1. Методы в массиве
      array.add(Color("Green"))  // Not Possible - cannot change size

array[0] = Color("Green")  // Possible - array object can be changed
array[0].name = "Green"   // Possible - object modification allowed by its class
  1. Методы в списке
      list.add(Color("Green")) // Not Possible - cannot change size
list[0] = Color("Green") // Not Possible - list object cannot be changed

list[0].name = "Green"   // Possible - object modification allowed by its class
  1. Методы в mutableList
      mutableList.add(Color("Green"))   // Possible - can change size
mutableList[0] = Color("Green")   // Possible - can change object
mutableList[0].name = "Green"   // Possible - object modification allowed by its class

Выводы :

  1. массивы и списки имеют фиксированный размер, поэтому вы не можете добавлять или удалять элементы.
  2. список не может быть изменен, что означает, что вы не можете изменить объект, который он содержит.
  3. массив можно изменить, что означает, что вы можете изменить объект, который он содержит.
  4. mutableList может делать что угодно, изменять размер или свои объекты

ПРИМЕЧАНИЕ. Это ничего не указывает на изменчивость самого объекта. т.е. если класс данных имеет какое-то свойство как изменяемое (var), его свойство можно изменить.

** В общем, различия между типами List и Array: **

List<...>:

только для чтения.

Array<...>:

Вы можете изменить это, или добавить что-то.

Другие вопросы по тегам