Vous voulez voir les jeux que j'ai réalisé sur Android ? C'est par là : Pixel Hideout sur Google Play (attention les graphismes piquent les yeux)

Passer de Java à Scala – Partie 2 – Tableaux et collections

Passer de Java à Scala – Partie 2 – Tableaux et collections

Dans cette seconde partie du tutoriel « Passer de Java à Scala », nous allons voir comment utiliser les différentes collections de Scala que sont les Array, les Map, les Set, les List et les Tuples.

Si vous ne l’avez pas déjà fait, je vous invite à consulter la 1ere partie de la série présentant le shell scala ainsi que l’article présentant globalement le passage de Java à Scala.

Retrouvez le code source des exemples ici : https://github.com/alexandrelanglais/scala-tutorial-2

Retrouvez tous les articles de la série « Passer de Java à Scala »

Collections en Scala

Les tableaux

On peut définir un tableau de plusieurs façon en Scala. Par exemple :

val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n"

Ici on assigne à la variable immutable greetStrings un nouveau tableau de String de taille 3. On assigne ensuite une par une les valeurs de ce dernier.

Il est possible d’être encore plus explicite et de spécifier le type de la variable à la déclaration :

val greetStrings:Array[String] = new Array[String](3)

Mais vu la détermination automatique de type dont sait faire preuve Scala, cette dernière expression n’est pas nécessaire.

Comme tout est objet en Scala, on peut expliquer l’usage de parenthèses () au lieu de crochets [] pour accéder aux valeurs d’un tableau : il s’agit en fait d’un appel d’une méthode (en l’occurence update)

Au vu de la note ci-dessus, écrire

greetStrings(0) = "Hello"

Est équivalent à écrire

greetStrings.update(0, "Hello")

Scala simplifie considérablement le travail du développeur de par sa capacité à substituer la concision à la verbosité.

Il est possible d’être plus concis encore pour écrire le code d’initialisation du tableau précédent :

val numNames = Array("zero", "one", "two")
for (i <- 0 to 2)
  println(numNames(i))

Une fois de plus, on pourrait spécifier le type du tableau via numName:Array[String].

Ceci est d’ailleurs nécessaire si l’on souhaite s’assurer que le tableau ne contienne que des String.

En effet, le programme suivant :

// Tableau de n'importe quoi
val anything = Array(1, "Hello", java.math.BigDecimal.ZERO)
for (i <- 0 to 2)
  println(anything(i).getClass)

Générera la sortie :

class java.lang.Integer
class java.lang.String
class java.math.BigDecimal

Il s’agit en réalité d’un tableau de type Array[Any], Any en Scala étant l’équivalent du Object de Java.

Pour forcer la paramétrisation du tableau, il faut écrire :

// Erreur de compilation : Array[Any] ne correspond pas à Array[String]
val neCompilePas:Array[String] = Array(1, "Hello", java.math.BigDecimal.ZERO)

Utilisation des listes

L’un des principes de la programmation fonctionnelle est que les méthodes ne devraient pas avoir d’effet de bord : elles devraient uniquement servir à retourner une valeur.

En programmation fonctionnelle, on privilégie les val aux var car elles sont immutables. De la même manière, en Scala on privilégie les Collections (List, Map, Set) car par défaut, elles sont immutables (contrairement aux Array).

L’initialisation d’une liste se fait comme pour un tableau :

// Creation et initialisation
val oneTwoThree = List(1, 2, 3)

Immutabilité des listes

Cette liste est immutable. Il n’y a pas de méthode update() et on ne peut écrire

oneTwoThree(0) = 4 // erreur de compilation

Si l’on veut rajouter des éléments à cette liste, on doit en créer une nouvelle et utiliser l’opérateur de concaténation ::: :

// Concaténation de listes
val fourFiveSix = List(4, 5, 6)
val concatenation = oneTwoThree ::: fourFiveSix

concatenation.foreach(s => { print(s); print(" ") })

On obtiendra en sortie :

1 2 3 4 5 6

Les listes oneTwoThree et fourFiveSix n’ont pas changé. On a créé la nouvelle liste concatenation à partir des 2 premières.

L’opérateur le plus commun avec les listes est le double deux-points :: aussi appelé cons. Il permet d’ajouter un élément en début de liste :

val zeroOneTwoThree = 0 :: oneTwoThree
zeroOneTwoThree.foreach(s => { print(s); print(" ") })

L’opérateur :: s’applique à la liste. Il est particulier car au lieu d’être suffixé à la liste via un point . comme on en a l’habitude en Java, celui-ci est préfixé.

Nil

Le mot Nil représente une liste vide. Au lieu d’écrire

val l = List()

On peut simplement écrire

val l = Nil

Pour voir le chainage de l’opérateur cons :: on peut définir une liste en utilisant Nil de la manière suivante :

val nilList = 1 :: 2 :: 3 :: Nil

En lisant de droite à gauche, la list Nil effectue l’opération :: 3 retournant une liste, sur laquelle on peut enchainer les ::.

Pas de méthode pour append ?

Bien qu’il soit possible de faire un append sur une liste via l’opérateur += cette pratique est déconseillée car peu performante :

// Ajouter un élément en fin de liste
val appended = nilList :+ 4

Il vaut mieux faire un reverse puis ajouter l’élément en premier, puis reverse de nouveau :

// Meilleur ajout d'un élément en fin de liste
val appendedNicely = (4 :: nilList.reverse).reverse

Fonctions de List

Retrouvez toutes les fonctions de List sur la documentation officielle ici : https://www.scala-lang.org/api/current/scala/collection/immutable/List.html


Utilisation des tuples

Les tuples sont des listes non paramétrisées. Ils sont surtout utiles lorsque l’on souhaite stocker des données de types différents sans avoir à créer de classe bean les englobant :

// Utilisation d'un tuple
val carte = (10, "pique")
println(carte._1)
println(carte._2)

Affichera

10
pique

Sets et Maps

Sets

Scala propose un trait pour les Set. Un trait est semblable à une interface en Scala (nous y reviendrons dans un prochain article).

Les collections sont séparées en 2 catégories : les mutables (modifiables) et les immutables (non-modifiables) comme exposé dans le graphique ci-dessous :

Sans autre spécification, la classe utilisée par défaut pour les Set est celle en bas à gauche, un HashSet immutable.

Un Set propose une méthode / opérateur + pour y ajouter des éléments. Cependant, le Set doit être mutable pour que cela fonctionne sur un set déclaré en val :

// Utilisation d'un set immutable 
var kaSet = Set("Audio", "Video")
kaSet += "Blueray"
println(kaSet.contains("Blueray"))

// déclaré en val
val immutableSet = Set("Audio", "Video")
immutableSet += "Blueray" // erreur de compilation : réassignation de variable

Pour utiliser un Set mutable, il faut le spécifier en important la classe en haut du fichier ou en le déclarant explicitement :

import scala.collection.mutable.Set

object ScalaMutableSet extends App {
  // mutable set
  val mutableSet = Set("Audio", "Video")
  mutableSet += "Blueray" // compile
}

Sur une collection immutable, l’opérateur + créé un nouvel objet. On peut par exemple écrire

import scala.collection.immutable.HashSet

object ScalaImmutableSet extends App {
  val hashSet = HashSet("Tomates", "Concombres")

  println(hashSet + "Patates de terre")
  println(hashSet)
}

L’output sera

Set(Patates de terre, Tomates, Concombres)
Set(Tomates, Concombres)

(Oui je sais on dit pommes de terre ou patates mais j’aime bien mélanger les 2 😉 )

Remarquez comme l’opérateur + a placé l’élément en début de Set pour des raisons de performances.

Maps

Comme pour les Set, Scala fournit des traits de base pour les maps :

Voyons comment on peut utiliser une mutable map :

import scala.collection.mutable.Map

object ScalaMutableMap extends App {
  // mutable map
  val mutableMap = Map[Int, String]()
  mutableMap += (1 -> "Ceci est")
  mutableMap += (2 -> "une")
  mutableMap += (3 -> "mutable map")

  println(mutableMap(3))
}

Le résultat :

mutable map

L’opérateur += de Map permet d’ajouter des éléments à celle-ci. Elle prend en paramètre une paire clé -> valeur dont les types correspondent à la déclaration de la Map[Int, String].

Enfin on note que le .get est implicite pour mutableMap(3).


Conclusion

Voilà qui clôt ce chapitre sur les Collections en Scala. Dans la prochaine partie nous parlerons des classes et des objets.

Laisser un commentaire