Collection

https://kotlinlang.org/docs/collections-overview.html
https://kotlinlang.org/docs/constructing-collections.html
Array

Класс для работы с массивами фиксированного размера. Он хранит элементы одного типа и предоставляет индексированный доступ к ним.

val array = arrayOf(1, 2, 3)
array[0] = 10 // Изменение элемента
println(array[0]) // 10
contentToString

Преобразует массив в строку.

// Пример для массива
val numbers = arrayOf(1, 2, 3, 4, 5)
val stringRepresentation = numbers.contentToString()
println(stringRepresentation) // Output: [1, 2, 3, 4, 5]
Iterable
https://kotlinlang.org/docs/iterators.html

Интерфейс коллекции элементов, которые можно перебрать.

MutableIterable

Интерфейс, расширяющий Iterable. Позволяет не только перебирать элементы, но и удалять их.

Collection

Неизменяемую коллекцию элементов.

val collection: Collection<Int> = listOf(1, 2, 3)
println(collection.contains(2)) // true
MutableCollection

Расширяет Collection, добавляя методы для изменения коллекции.

val mutableCollection: MutableCollection<Int> = mutableListOf(1, 2, 3)
mutableCollection.add(4)
mutableCollection.remove(2)
println(mutableCollection) // [1, 3, 4]
List
https://kotlinlang.org/docs/list-operations.html

Неизменяемый список.

val list = listOf(1, 2, 3)
println(list[0]) // 1
MutableList

Изменяемый список.

val list = mutableListOf(1, 2, 3)
list.add(4)
list[1] = 5
println(list) // [1, 5, 3, 4]
Set
https://kotlinlang.org/docs/set-operations.html

Неизменяемая коллекция уникальных элементов.

val set = setOf(1, 2, 3, 3)
println(set) // [1, 2, 3]
MutableSet

Изменяемая коллекция уникальных элементов.

val set = mutableSetOf(1, 2, 3)
set.add(4)
println(set) // [1, 2, 3, 4]
Map
https://kotlinlang.org/docs/map-operations.html

Изменяемый ассоциативный массив.

val map = mapOf("a" to 1, "b" to 2)
println(map["a"]) // 1
MutableMap

Изменяемый ассоциативный массив.

val map: MutableMap<String, Int> = mutableMapOf()
map["one"] = 1  // Добавление
map.put("two", 2)  // Альтернативный способ
println(map["one"])  // Выводит: 1
map.remove("one")  // Удаление
println(map)  // Выводит: {two=2}
Sequences
https://kotlinlang.org/docs/sequences.html
04.04.2022https://www.raywenderlich.com/31290959-kotlin-sequences-getting-started

Последовательности представляет собой ленивую коллекцию, которая позволяет эффективно работать с большими объемами данных или сложными цепочками преобразований. Основная особенность Sequence — это ленивое выполнение, что означает, что элементы вычисляются по мере необходимости, а не сразу.

• Вычисляет элементы только при необходимости, а не сразу при создании. Это позволяет избежать лишних вычислений и экономит ресурсы. Это особенно полезно при работе с большими данными или при выполнении сложных цепочек преобразований.

• Методы, такие как map, filter, и flatMap возвращают новый Sequence, который будет вычисляться при итерации. Преобразования применяются по цепочке, что минимизирует количество проходов по данным.

• Sequence предоставляет возможности для работы с данными, которые могут быть получены из различных источников, таких как коллекции, потоки данных и т.д.

// Элементы фильтруются и преобразуются по мере необходимости, а не сразу.
val numbers = (1..10).asSequence()
val result = numbers
    .filter { it % 2 == 0 }
    .map { it * it }
    .toList()

println(result) // Output: [4, 16, 36, 64, 100]
Отличие от коллекций

Основное различие между Sequences и коллекциями в Kotlin заключается в способе обработки данных: ленивое вычисление в последовательностях и жадное вычисление в коллекциях. Sequence более подходит для ленивых вычислений и сложных цепочек преобразований, в то время как коллекции подходят для простых задач с быстрым доступом к данным.

Коллекции:

• Операции, такие как map, filter, и другие, выполняются сразу над всеми элементами коллекции.

• При каждом преобразовании (например, при применении нескольких последовательных функций map, filter) коллекция обрабатывается целиком на каждом шаге. Это может приводить к нескольким проходам по данным.

• Эффективны для небольших наборов данных или когда нужно быстро получить результат сразу после выполнения всех операций.

• Поддерживают произвольный доступ (например, list[2]), что делает их удобными для операций, зависящих от индексов.

// Сначала вся коллекция умножается на 2, а затем результат фильтруется, что приводит к двум проходам по данным.
val result = listOf(1, 2, 3, 4, 5)
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .toList()

Последовательности:

• Выполняются лениво, что означает, что операции, такие как map или filter, откладываются до момента, когда результат действительно нужен (например, когда вызывается терминальная операция вроде toList).

• Все преобразования выполняются поэтапно для каждого элемента. Например, при цепочке преобразований элемент обрабатывается через все этапы сразу, что может сэкономить ресурсы.

• Более эффективны для работы с большими объемами данных или сложными цепочками операций, так как они не выполняют лишние вычисления и используют память экономнее.

• Работают с последовательными операциями и не поддерживают произвольный доступ, что может сделать их менее удобными для некоторых задач.

// Каждый элемент будет умножен на 2, проверен на делимость на 3 и сохранен в результат, что минимизирует количество операций.
val result = listOf(1, 2, 3, 4, 5)
    .asSequence()
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .toList()
asSequence

Преобразует коллекцию в Sequence.

val list = listOf(1, 2, 3, 4, 5)
val sequence = list.asSequence()
map

Применяет функцию преобразования к каждому элементу.

val transformedSequence = sequence.map { it * 2 }
filter

Отбирает элементы, удовлетворяющие условию.

val filteredSequence = sequence.filter { it % 2 == 0 }
generateSequence

Позволяет создавать бесконечные последовательности, которые можно ограничить с помощью методов, таких как take.

val infiniteSequence = generateSequence(1) { it + 1 }
val firstFive = infiniteSequence.take(5).toList()
println(firstFive) // Output: [1, 2, 3, 4, 5]
Operators
forEach

Используется для выполнения действия над каждым элементом коллекции.

val numbers = listOf(1, 2, 3, 4)
numbers.forEach { number ->
    println(number)  // Выведет: 1, 2, 3, 4
}
forEachIndexed

Позволяет итерировать по элементам коллекции с доступом к их индексу.

val fruits = listOf("Apple", "Banana", "Cherry")
fruits.forEachIndexed { index, fruit ->
    println("Index $index: $fruit")  // Выведет: Index 0: Apple, Index 1: Banana, Index 2: Cherry
}
map

Преобразует элементы коллекции, создавая новую коллекцию.

val doubled = numbers.map { it * 2 }  // [2, 4, 6, 8]
filter

Создает новую коллекцию, содержащую только элементы, соответствующие условию.

val evenNumbers = numbers.filter { it % 2 == 0 }  // [2, 4]
reduce

Выполняет агрегирующую операцию над элементами коллекции.

val sum = numbers.reduce { acc, number -> acc + number }  // 10
fold

Похож на reduce, но позволяет задать начальное значение.

val sumWithInitial = numbers.fold(0) { acc, number -> acc + number }  // 10
flatMap

Применяет преобразование к каждому элементу и объединяет результаты в одну коллекцию.

val nestedList = listOf(listOf(1, 2), listOf(3, 4))
val flatList = nestedList.flatMap { it }  // [1, 2, 3, 4]
distinct

Используется для получения уникальных элементов из коллекции. Этот оператор возвращает новую коллекцию, содержащую только неповторяющиеся элементы, сохраняя при этом порядок их появления в исходной коллекции.

val numbers = listOf(1, 2, 2, 3, 4, 4, 5)
val distinctNumbers = numbers.distinct() // [1, 2, 3, 4, 5]
associate

Преобразует элементы коллекции в ключи и значения на основе переданной лямбды.

val list = listOf("apple", "banana", "cherry")
val map = list.associate { it.first() to it.length }
println(map) // {a=5, b=6, c=6}
associateBy

Cоздает мапу, где ключи определяются лямбдой, а значения остаются элементами коллекции.

val list = listOf("apple", "banana", "cherry")
val map = list.associateBy { it.first() }
println(map) // {a=apple, b=banana, c=cherry}
Вопросы на собесе (15)
  • Collections (10)
    1. Kакие типы коллекции существуют в Kotlin?

      List MutableList Set MutableSet Map MutableMap

    1. Зачем в Kotlin коллекции делятся на mutable и immutable?

      Предотвращается изменение данных при передаче, улучшается предсказуемость и потокобезопасность в многопоточном окружении без дополнительных синхронизаций.

    1. Разница между mutable и immutable коллекциями в Kotlin?

      Mutable коллекции позволяют изменять свои элементы, тогда как immutable коллекции не допускают изменений после создания, требуя создания новых экземпляров при модификации.

    1. Во что Mutable-коллекции Kotlin превращаются в Java?

      Mutable-коллекции в Kotlin превращаются в стандартные изменяемые коллекции Java.

      MutableList становится java.util.List.

      MutableSet превращается в java.util.Set.

      MutableMap становится java.util.Map.

    1. Как работает функция map для коллекций в Kotlin?

      Применяет функцию transform к каждому элементу исходной коллекции и возвращает новую коллекцию.

    1. Как получить уникальные объекты из списка?

      Использовать оператор distinct(). Этот метод сохраняет порядок элементов и убирает дубликаты.

    1. Как написать свою функцию map для коллекции?

      Вот так, используя inline:

      inline fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
          val result = mutableListOf<R>()
          for (item in this) {
              result.add(transform(item))
          }
          return result
      }
    1. Какой будет тип и что выведется?
      var shortChars = listOf("1", "2")
      var shortNumbers2 = listOf(1, 2)
      println(
      		shortChars
      				.reversed()
      				.map { it.toInt() }
      				.zip(shortNumbers2) { a, b ->
      						"$a+$b"
      				}
      )

      Тип будет List. Вывод: [2+1, 1+2].

    1. Что напечатает println?
      val map = mutableMapOf("ZeroKey" to "0")
          .plus(
              "FirstKey" to "1",
          ).apply {
              this.plus("SecondKey" to "2")
          }
      println(map.values)

      [0]

    1. Будет ли работать это код, как исправить?
      val moviesYears = mutableListOf(
          1999, 2001, 2012,
          2014, 2015, 2017
      )
      
      for (year in moviesYears)
          if (year >= 2014)
              moviesYears.remove(year)

      Код не будет работать корректно, так как нельзя изменять коллекцию (moviesYears) во время её итерации — это может привести к пропуску элементов. Исправить можно, используя removeIf.

  • Sequences (5)
    1. Для чего нужен Sequence?

      Для ленивой обработки коллекций, когда операции применяются к элементам по мере их запроса, что позволяет экономить память и избегать лишних вычислений, особенно с большими наборами данных.

    1. Как Sequence работает с точки зрения памяти?

      Sequence использует меньше памяти в хипе, так как обрабатывает элементы лениво, создавая их только по мере необходимости и не загружая всю коллекцию в память сразу. Каждый вызов последовательной операции над Sequence создаёт объект, который ссылается на следующий элемент, освобождая память по мере завершения обработки текущих элементов.

    1. Разница между Collection и Sequences?

      Коллекции выполняют все операции сразу, сохраняя промежуточные результаты в памяти, тогда как последовательности обрабатывают элементы лениво, только по мере необходимости, что позволяет работать с большими наборами данных более эффективно.

    1. В каких случаях Sequence лучше Collections?

      Sequence лучше подходит, когда нужно обработать большие наборы данных или выполнить несколько операций последовательно, избегая создания промежуточных коллекций. Она вычисляется лениво, что помогает сэкономить память и ускорить выполнение в таких случаях.

    1. Как будут выполняться эти операторы? Как улучшить метод чтобы было меньше накладных расходов?
      fun foo3(items: MutableList<Item>) {
      		return items
      				.filterIsInstance<ShiftInterval>()
      				.filter { it.isChecked }
      				.groupBy(...)
      				.mapKeys { ... }
      				.flatMap { ... }
      }

      Операторы будут выполняться последовательно, каждый создавая промежуточные коллекции. Чтобы уменьшить накладные расходы, можно заменить последовательность операций на ленивую обработку с использованием последовательностей (Sequence), что позволит избежать создания промежуточных коллекций. Использование items.asSequence() делает операции ленивыми и уменьшает накладные расходы, так как коллекции не будут создаваться на каждом этапе.