La bibliothèque standard de Kotlin fournit un ensemble complet d’outils de gestion collections – des groupes d’un nombre variable d’éléments (éventuellement zéro) qui partagent une signification pour le problème à résoudre et sont exploités en commun.

Les collections sont un concept courant pour la plupart des langages de programmation, donc si vous connaissez, par exemple, les collections Java ou Python, vous pouvez ignorer cette introduction et passer aux sections détaillées.

Une collection contient généralement un certain nombre d’objets (ce nombre peut également être zéro) du même type. Les objets d’une collection sont appelés éléments ou éléments. Par exemple, tous les étudiants d’un département forment une collection qui permet de calculer leur âge moyen. Les types de collecte suivants sont pertinents pour Kotlin :

  • Lister est une collection ordonnée avec accès aux éléments par des indices – des nombres entiers qui reflètent leur position. Les éléments peuvent apparaître plusieurs fois dans une liste. Un exemple de liste est une phrase : c’est un groupe de mots, leur ordre est important, et ils peuvent se répéter.
  • Régler est une collection d’éléments uniques. Il reflète l’abstraction mathématique de l’ensemble : un groupe d’objets sans répétitions. Généralement, l’ordre des éléments d’ensemble n’a aucune signification. Par exemple, un alphabet est un ensemble de lettres.
  • Carte (ou dictionnaire) est un ensemble de paires clé-valeur. Les clés sont uniques et chacune d’elles correspond exactement à une valeur. Les valeurs peuvent être des doublons. Les cartes sont utiles pour stocker des connexions logiques entre des objets, par exemple, l’ID d’un employé et son poste.

Kotlin vous permet de manipuler des collections indépendamment du type exact d’objets qui y sont stockés. En d’autres termes, vous ajoutez un String à une liste de StringC’est de la même manière que vous le feriez avec Ints ou une classe définie par l’utilisateur. Ainsi, la bibliothèque standard de Kotlin propose des interfaces, des classes et des fonctions génériques pour créer, remplir et gérer des collections de tout type.

Les interfaces de collection et les fonctions associées se trouvent dans le package kotlin.collections. Faisons un tour d’horizon de son contenu.

Types de collecte

La bibliothèque standard de Kotlin fournit des implémentations pour les types de collections de base : ensembles, listes et cartes. Une paire d’interfaces représente chaque type de collection :

  • UNE lecture seulement interface qui fournit des opérations pour accéder aux éléments de collection.
  • UNE mutable interface qui étend l’interface en lecture seule correspondante avec des opérations d’écriture : ajout, suppression et mise à jour de ses éléments.

Notez que la modification d’une collection mutable n’exige pas qu’elle soit une var: les opérations d’écriture modifient le même objet de collection mutable, de sorte que la référence ne change pas. Bien que, si vous essayez de réaffecter un val collection, vous obtiendrez une erreur de compilation.

fun main() {
//sampleStart
    val numbers = mutableListOf("one", "two", "three", "four")
    numbers.add("five")   // this is OK    
    //numbers = mutableListOf("six", "seven")      // compilation error
//sampleEnd
}

Les types de collection en lecture seule sont covariants. Cela signifie que, si un Rectangle la classe hérite de Shape, vous pouvez utiliser un List<Rectangle> n’importe où le List<Shape> est requis. En d’autres termes, les types de collection ont la même relation de sous-typage que les types d’élément. Les mappages sont covariants sur le type de valeur, mais pas sur le type de clé.

À leur tour, les collections mutables ne sont pas covariantes ; sinon, cela conduirait à des échecs d’exécution. Si MutableList<Rectangle> était un sous-type de MutableList<Shape>, vous pouvez insérer d’autres Shape héritiers (par exemple, Circle) en elle, violant ainsi son Rectangle type argument.

Vous trouverez ci-dessous un schéma des interfaces de la collection Kotlin :

Hiérarchie des interfaces de collecte

Parcourons les interfaces et leurs implémentations.

Collection

Collection<T> est la racine de la hiérarchie de collection. Cette interface représente le comportement courant d’une collection en lecture seule : récupération de la taille, vérification de l’appartenance à un élément, etc. Collection hérite de la Iterable<T> interface qui définit les opérations d’itération des éléments. Vous pouvez utiliser Collection en tant que paramètre d’une fonction qui s’applique à différents types de collection. Pour des cas plus spécifiques, utilisez le Collectionhéritiers de : List et Set.

fun printAll(strings: Collection<String>) {
        for(s in strings) print("$s ")
        println()
    }
    
fun main() {
    val stringList = listOf("one", "two", "one")
    printAll(stringList)
    
    val stringSet = setOf("one", "two", "three")
    printAll(stringSet)
}

MutableCollection est un Collection avec des opérations d’écriture, telles que add et remove.

fun List<String>.getShortWordsTo(shortWords: MutableList<String>, maxLength: Int) {
    this.filterTo(shortWords) { it.length <= maxLength }
    // throwing away the articles
    val articles = setOf("a", "A", "an", "An", "the", "The")
    shortWords -= articles
}

fun main() {
    val words = "A long time ago in a galaxy far far away".split(" ")
    val shortWords = mutableListOf<String>()
    words.getShortWordsTo(shortWords, 3)
    println(shortWords)
}

Lister

List<T> stocke les éléments dans un ordre spécifié et leur fournit un accès indexé. Les indices partent de zéro – l’indice du premier élément – et vont à lastIndex qui est le (list.size - 1).

fun main() {
//sampleStart
    val numbers = listOf("one", "two", "three", "four")
    println("Number of elements: ${numbers.size}")
    println("Third element: ${numbers.get(2)}")
    println("Fourth element: ${numbers[3]}")
    println("Index of element "two" ${numbers.indexOf("two")}")
//sampleEnd
}

Les éléments de liste (y compris les valeurs NULL) peuvent dupliquer : une liste peut contenir un nombre quelconque d’objets égaux ou d’occurrences d’un seul objet. Deux listes sont considérées comme égales si elles ont les mêmes tailles et des éléments structurellement égaux aux mêmes positions.

data class Person(var name: String, var age: Int)

fun main() {
//sampleStart
    val bob = Person("Bob", 31)
    val people = listOf(Person("Adam", 20), bob, bob)
    val people2 = listOf(Person("Adam", 20), Person("Bob", 31), bob)
    println(people == people2)
    bob.age = 32
    println(people == people2)
//sampleEnd
}

MutableList<T> est un List avec des opérations d’écriture spécifiques à une liste, par exemple, pour ajouter ou supprimer un élément à une position spécifique.

fun main() {
//sampleStart
    val numbers = mutableListOf(1, 2, 3, 4)
    numbers.add(5)
    numbers.removeAt(1)
    numbers[0] = 0
    numbers.shuffle()
    println(numbers)
//sampleEnd
}

Comme vous le voyez, à certains égards, les listes sont très similaires aux tableaux. Cependant, il y a une différence importante : la taille d’un tableau est définie lors de l’initialisation et n’est jamais modifiée ; à son tour, une liste n’a pas de taille prédéfinie ; la taille d’une liste peut être modifiée à la suite d’opérations d’écriture : ajout, mise à jour ou suppression d’éléments.

Dans Kotlin, l’implémentation par défaut de List est ArrayList que vous pouvez considérer comme un tableau redimensionnable.

Régler

Set<T> stocke des éléments uniques ; leur ordre est généralement indéfini. null les éléments sont également uniques : un Set ne peut contenir qu’un null. Deux ensembles sont égaux s’ils ont la même taille, et pour chaque élément d’un ensemble, il y a un élément égal dans l’autre ensemble.

fun main() {
//sampleStart
    val numbers = setOf(1, 2, 3, 4)
    println("Number of elements: ${numbers.size}")
    if (numbers.contains(1)) println("1 is in the set")

    val numbersBackwards = setOf(4, 3, 2, 1)
    println("The sets are equal: ${numbers == numbersBackwards}")
//sampleEnd
}

MutableSet est un Set avec les opérations d’écriture de MutableCollection.

L’implémentation par défaut de SetLinkedHashSet – préserve l’ordre d’insertion des éléments. Par conséquent, les fonctions qui reposent sur l’ordre, telles que first() ou last(), renvoient des résultats prévisibles sur de tels ensembles.

fun main() {
//sampleStart
    val numbers = setOf(1, 2, 3, 4)  // LinkedHashSet is the default implementation
    val numbersBackwards = setOf(4, 3, 2, 1)
    
    println(numbers.first() == numbersBackwards.first())
    println(numbers.first() == numbersBackwards.last())
//sampleEnd
}

Une implémentation alternative – HashSet – ne dit rien sur l’ordre des éléments, donc appeler de telles fonctions dessus renvoie des résultats imprévisibles. Cependant, HashSet nécessite moins de mémoire pour stocker le même nombre d’éléments.

Carte

Map<K, V> n’est pas héritier de la Collection interface; Cependant, il s’agit également d’un type de collection Kotlin. UNE Map magasins valeur clé paires (ou entrées); les clés sont uniques, mais différentes clés peuvent être associées à des valeurs égales. Les Map L’interface fournit des fonctions spécifiques, telles que l’accès à la valeur par clé, la recherche de clés et de valeurs, etc.

fun main() {
//sampleStart
    val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
    
    println("All keys: ${numbersMap.keys}")
    println("All values: ${numbersMap.values}")
    if ("key2" in numbersMap) println("Value by key "key2": ${numbersMap["key2"]}")    
    if (1 in numbersMap.values) println("The value 1 is in the map")
    if (numbersMap.containsValue(1)) println("The value 1 is in the map") // same as previous
//sampleEnd
}

Deux cartes contenant les paires égales sont égales quel que soit l’ordre des paires.

fun main() {
//sampleStart
    val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)    
    val anotherMap = mapOf("key2" to 2, "key1" to 1, "key4" to 1, "key3" to 3)
    
    println("The maps are equal: ${numbersMap == anotherMap}")
//sampleEnd
}

MutableMap est un Map avec les opérations d’écriture de carte, par exemple, vous pouvez ajouter une nouvelle paire clé-valeur ou mettre à jour la valeur associée à la clé donnée.

fun main() {
//sampleStart
    val numbersMap = mutableMapOf("one" to 1, "two" to 2)
    numbersMap.put("three", 3)
    numbersMap["one"] = 11

    println(numbersMap)
//sampleEnd
}

L’implémentation par défaut de MapLinkedHashMap – préserve l’ordre d’insertion des éléments lors de l’itération de la carte. À son tour, une mise en œuvre alternative – HashMap – ne dit rien sur l’ordre des éléments.