Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici

Les collections

Introduction

Les tableaux permettent de stocker plusieurs valeurs d’un même type en leur sein (voir le chapitre Les tableaux). S’ils sont très utiles, ils ont aussi des limitations. Par exemple, un tableau a une taille fixe définie lors de son initialisation et il n’est pas possible, en cas de besoin, de réduire ou d’agrandir facilement sa taille.

Pour s’affranchir de cette contrainte, il existe des structures de données bien plus puissantes que les tableaux : les collections.

Bien évidemment, l’avantage des collections ne se limite pas à la possibilité d’adapter la taille et donc le nombre d’éléments stockés. Leurs atouts sont bien plus nombreux.

Dans ce chapitre, nous allons étudier six collections que l’on peut classer en trois familles : la première regroupe les classes List et MutableList ; la deuxième, les classes Set et MutableSet ; et la troisième, les classes Map et MutableMap.

List et MutableList

Débutons notre étude des collections avec les listes, et plus particulièrement les deux classes List et MutableList.

1. Qu’est-ce qu’une liste ?

Comme leurs noms l’indiquent, ces deux classes permettent de modéliser des listes. Le but d’une liste est simple : stocker des éléments à un indice donné.

Le principe est finalement identique à celui des tableaux, si ce n’est que la taille d’une liste est dynamique puisqu’elle se met à jour automatiquement, sans action de la part du développeur.

Il n’y a pas de règles spécifiques qui régissent le fonctionnement des listes. Par exemple, il est tout à fait possible de stocker dix fois le même élément au sein d’une liste.

Comme le langage Kotlin propose systématiquement une version de ses collections en lecture seule et une version accessible en lecture et en écriture, nous avons, dans le cas des listes, la classe List qui permet de représenter une liste en lecture seule, et la classe MutableList qui permet de représenter une liste accessible en lecture et en écriture. Les fonctionnalités proposées par ces deux classes sont donc légèrement différentes.

2. Les listes en lecture seule

Dans cette section, nous allons voir comment déclarer une liste en lecture seule et les différentes opérations disponibles sur ce genre de liste.

a. Déclarer une liste en lecture seule

Pour déclarer une liste en lecture seule, il convient d’utiliser la fonction listOf. Puisqu’on parle ici d’une liste en lecture seule, il est nécessaire de renseigner les éléments qui composent la liste au moment de son initialisation. Ces valeurs sont à renseigner en tant qu’arguments de la fonction listOf.

À l’instar des tableaux, les listes acceptent de nombreux types, aussi bien des objets que des types primitifs.

Exemples

val listOfInts = listOf(15, 1024, 1, -8) 
val listOfStrings = listOf("a string", "another string", "Heyden") 
val listOfDogs = listOf(Dog("Doggo"), Dog("Heyden")) 
val listOfLongs = listOf(-1L, 1478L, 1L, 0L) 
val listOfDouble = listOf(1.8, 0.0, -17.6896) 

b. Parcourir une liste

Qu’il s’agisse...

Set et MutableSet

Après les listes, nous allons voir dans cette section un deuxième type de collection à travers deux interfaces : Set et MutableSet.

1. Qu’est-ce qu’un “set” ?

Un set permet de stocker des éléments de manière unique. Ce qui revient à dire que cette structure de données ne peut pas contenir de doublons, Set et MutableSet sont des interfaces, là où List et MutableList sont des classes.

Le langage Kotlin nous offre deux implémentations de ces interfaces.

La première est disponible via la classe LinkedHashSet. Il s’agit de l’implémentation par défaut proposée par le langage Kotlin. Dans le cadre de cet ouvrage, nous nous limiterons à l’étude et à l’utilisation de cette implémentation. Elle conserve l’ordre d’insertion des éléments.

La seconde est disponible via la classe HashSet. Elle ne conserve pas l’ordre d’insertion des éléments. L’utilisation de cette classe est plus optimisée, car elle nécessite moins de mémoire.

Comme pour les listes, le langage Kotlin propose une version de la collection en lecture seule et une version accessible en lecture et en écriture. L’interface Set permet de représenter un set en lecture seule tandis que l’interface MutableSet permet de représenter un set sur lequel il est possible d’effectuer des opérations de lecture et d’écriture. Les fonctionnalités proposées par ces deux interfaces sont donc légèrement différentes.

2. Les sets en lecture seule

Dans cette section, nous allons voir comment déclarer un set en lecture seule et les différentes opérations disponibles sur ce type de collection.

a. Déclarer un set en lecture seule

Pour déclarer un set en lecture seule, il convient d’utiliser la fonction setOf. Puisqu’on parle ici d’un set en lecture seule, il est nécessaire au moment de son initialisation de renseigner les éléments qui le composent, en tant qu’arguments de la fonction setOf.

Comme les listes, les set peuvent aussi bien contenir des objets que des types primitifs.

Exemples

val setOfInts = setOf(15, 1024, 1, -8) 
val setOfStrings...

Map et MutableMap

Terminons notre étude des collections avec deux nouvelles interfaces : Map et MutableMap.

Même si, d’un point de vue technique, ces deux interfaces ne sont pas des collections (car elles n’héritent pas de l’interface Collection), leur fonctionnement et leur utilisation sont très proches de ceux d’une collection. C’est pourquoi on les étudie généralement en même temps.

1. Qu’est-ce qu’une map ?

Les interfaces Map et MutableMap permettent de stocker des éléments sous la forme d’une paire clé-valeur.

La clé est utilisée comme identifiant. Cela signifie qu’une map ne peut pas contenir plusieurs paires ayant la même clé.

L’interface Map permet de représenter une map en lecture seule tandis que l’interface MutableMap permet de représenter une map sur laquelle il est possible d’effectuer des opérations de lecture et d’écriture. Les fonctionnalités proposées par ces deux interfaces sont donc légèrement différentes.

2. Les maps en lecture seule

Dans cette section, nous allons voir comment déclarer une map en lecture seule et les différentes opérations disponibles sur ce genre de map.

a. Déclarer une map en lecture seule

Pour déclarer une map en lecture seule, il convient d’utiliser la fonction mapOf. Puisqu’on parle ici d’une map en lecture seule, il est nécessaire de renseigner les éléments qui composent la map au moment de son initialisation. Ces valeurs sont à renseigner en tant qu’arguments de la fonction mapOf.

À l’instar des précédentes collections, les maps peuvent aussi bien contenir des objets que des types primitifs, et ce pour la clé ou la valeur.

Pour déclarer une paire, il convient d’utiliser la syntaxe suivante, où X doit être remplacé par la valeur de la clé et Y doit être remplacé par la valeur de la paire :

X to Y 

Exemples

val mapOfStringsInts = mapOf("Dog" to 4, "Cat" to 16) 
val mapOfLongsLongs = mapOf(-1L to 4L, 584L to 0L) 
val mapOfObjectsDouble = mapOf(Dog("Doggo") to 1.8, Dog("Heyden") 
to -17.6896) 

À quoi sert cette collection ?...

Les opérations sur les collections

Outre les opérations spécifiques aux trois types de collections vues au début de ce chapitre, il existe plusieurs opérations utilisables sur l’ensemble de ces collections ; par exemple, transformer, trier ou filtrer une collection.

Cette section a pour objectif de présenter les principales opérations, mais il n’est malheureusement pas possible d’être exhaustif. Reportez-vous à la documentation officielle du langage Kotlin pour un complément d’informations.

1. Transformer une collection

Une opération de transformation permet de construire une collection en appliquant des transformations à une autre collection. Par exemple, à partir d’une collection d’objets complexes, il est possible de construire une autre collection qui ne contiendra que les valeurs d’un attribut spécifique.

Dans ce qui suit, nous allons étudier deux opérations de transformation : le mapping et le zipping.

a. Le mapping

Le mapping est possible grâce à la fonction map. Cette fonction prend en argument une expression lambda qui permet de transformer l’élément courant. Elle retourne le résultat transformé. Cette fonction est appelée pour chacun des éléments qui composent la collection originale. Il est donc tout à fait possible d’imaginer des transformations différentes entre les éléments en fonction d’une règle métier choisie. L’élément courant est exposé au sein de l’expression lambda grâce au mot-clé it.

En guise d’exemple, transformons des chiens en chats !

Soit la classe Dog permettant de modéliser un chien :

data class Dog(val name: String) 

Soit la classe Cat permettant de modéliser un chat :

data class Cat(val name: String) 

Le programme suivant transforme les chiens en chats :

fun main() 
{ 
 val dogs = listOf(Dog("Doggo"), Dog("Heyden")) 
 val cats = dogs.map { Cat(it.name) } 
 
 println(cats) 
} 

Dans ce programme, on récupère l’attribut name des chiens de la liste dogs pour créer des instances de la classe Cat et ainsi transformer les chiens...

En résumé

  • Le langage Kotlin propose plusieurs types de collections : les listes, les sets et les maps.

  • Chaque collection est disponible sous la forme d’une collection accessible en lecture seule ou sous la forme d’une collection supportant des opérations de lecture et d’écriture.

  • Chaque collection propose des opérations qui lui sont propres.

  • Il est possible de passer d’une collection en lecture seule à une collection supportant des opérations de lecture et d’écriture, et inversement.

  • Les collections en lecture seule supportent le concept de covariance, ce qui n’est pas le cas des collections supportant les opérations de lecture et d’écriture. 

  • Le langage Kotlin autorise de nombreuses opérations sur les collections.

  • Les opérations de transformation permettent de construire une collection en appliquant des transformations à une autre collection.

  • Les opérations de filtrage permettent de filtrer une collection pour construire un sous-ensemble.

  • Les opérations de regroupement permettent de regrouper les éléments d’une collection sous la forme d’une map.

  • Les opérations de sélection permettent de sélectionner un élément ou un ensemble d’éléments.

  • Les opérations de tri permettent de trier les éléments d’une collection....