Skip to main content

Code that compiles on the first try! CodeWithMpia wishes you very happy holidays.✨

Aller au contenu principal

Maîtriser les collections en Dart : List, Set, Map et leurs méthodes puissantes

Un guide pratique pour maîtriser les collections en Dart. Découvre les différences entre List, Set et Map, apprends les méthodes puissantes de transformation (map, where, reduce), comprends quand utiliser quelle structure, et optimise les performances de ton code.

M
Mpia
12/22/2025 44 min
84 vues
1 comme.
#List#Collections#Map#Set
Maîtriser les collections en Dart : List, Set, Map et leurs méthodes puissantes

Salut ! Aujourd'hui, on va plonger dans un sujet fondamental en Dart : les collections. Si tu codes en Dart (et donc probablement en Flutter), tu manipules des collections tout le temps - des listes de produits, des maps de configurations, des sets d'IDs uniques...

Le truc, c'est que Dart offre des méthodes ultra-puissantes pour manipuler les collections de façon élégante. Mais beaucoup de développeurs passent à côté et écrivent du code verbeux avec des boucles for partout.

Dans cet article, je vais te montrer comment utiliser les collections comme un ninja. Tu vas découvrir des méthodes que tu ne connaissais peut-être pas, comprendre quand utiliser List vs Set vs Map, et surtout, écrire du code plus concis et plus performant.

Prêt à devenir un magicien des collections ? C'est parti ! 🚀

Les trois types de collections

Avant de plonger dans les détails, comprenons bien les différences entre les trois types principaux de collections en Dart.

List : la collection ordonnée

Une List est comme un tableau. Elle stocke des éléments dans un ordre précis et permet les doublons.

Quand l'utiliser ?

  • Tu veux garder l'ordre d'insertion

  • Tu as besoin d'accéder aux éléments par index

  • Les doublons, c'est ok pour toi

Exemple d'usage : Une liste de messages dans un chat, une file d'attente de tâches, un historique de navigation.

List<String> messages = [
  'Bonjour',
  'Comment ça va ?',
  'Bonjour', // Doublon autorisé
];

print(messages[0]); // Accès par index
print(messages.length); // Taille de la liste

Set : la collection d'éléments uniques

Un Set c'est cool parce que ça te garantit qu'il y a pas de doublons. L'ordre n'est pas garanti (sauf avec LinkedHashSet).

Quand l'utiliser ?

  • Tu veux éliminer les doublons automatiquement

  • Tu fais beaucoup de vérifications d'existence (contains)

  • Tu veux faire des opérations mathématiques (union, intersection)

Exemple d'usage : Liste d'IDs uniques, tags associés à un article, ensemble de permissions.

Set<String> tags = {'dart', 'flutter', 'mobile'};
tags.add('dart'); // Ignoré car déjà présent
print(tags.length); // 3

// Vérifier si un truc existe - super rapide !
bool contient = tags.contains('flutter'); // true

Map : la collection clé-valeur

Une Map (ou dictionnaire) c'est ta meilleure amie quand tu dois associer des clés à des valeurs. C'est parfait pour des lookups rapides.

Quand l'utiliser ?

  • T'as des paires clé-valeur

  • Tu veux accéder vite à une valeur via sa clé

  • Tu modélises des configs, des traductions, des caches

Exemple d'usage : Configuration d'app, données d'utilisateur structurées, cache de résultats.

Map<String, int> ages = {
  'Alice': 25,
  'Bob': 30,
  'Charlie': 35,
};

print(ages['Alice']); // Accès super rapide

List : bien plus qu'un simple tableau

Commençons par explorer les List en profondeur. Dart offre deux types de listes : les listes qui peuvent grandir (growable) et les listes à taille fixe.

Créer des listes

// Liste vide (peut grandir)
List<int> nombres = [];

// Liste avec des valeurs
List<String> fruits = ['Pomme', 'Banane', 'Orange'];

// Liste typée explicitement
List<double> prix = [9.99, 14.50, 7.25];

// Liste avec le type deviné
var couleurs = ['Rouge', 'Vert', 'Bleu'];

// Liste à taille fixe (attention !)
List<int> fixe = List.filled(5, 0); // [0, 0, 0, 0, 0]
// fixe.add(1); // ERREUR : taille fixe, tu peux pas ajouter

// Liste générée
List<int> carres = List.generate(5, (index) => index * index);
// [0, 1, 4, 9, 16]

La différence entre liste variable et fixe c'est important. Une liste créée avec List.filled() ne peut pas grandir ou rétrécir. Si tu essaies d'ajouter un élément, ton app va crasher.

Accéder aux éléments

List<String> fruits = ['Pomme', 'Banane', 'Orange', 'Mangue'];

// Par index
print(fruits[0]); // Pomme
print(fruits[2]); // Orange

// Premier et dernier
print(fruits.first); // Pomme
print(fruits.last); // Mangue

// Pas d'index négatifs en Dart, mais tu peux faire :
print(fruits[fruits.length - 1]); // Dernier élément

// Accès sécurisé (évite les crashs)
String? element = fruits.length > 5 ? fruits[5] : null;

// Sous-liste (range)
List<String> sousListe = fruits.sublist(1, 3); // ['Banane', 'Orange']

Attention aux accès hors limites ! Si tu essaies fruits[10] sur une liste de 4 éléments, ton app va crasher direct. Toujours bien vérifier avant ou utiliser des méthodes sécurisées.

Modifier des listes

List<String> taches = ['Coder', 'Tester'];

// Ajouter un élément
taches.add('Déployer');
print(taches); // ['Coder', 'Tester', 'Déployer']

// Ajouter plusieurs
taches.addAll(['Documenter', 'Réviser']);

// Insérer à une position spécifique
taches.insert(1, 'Débugger');
print(taches); // ['Coder', 'Débugger', 'Tester', ...]

// Modifier un élément
taches[0] = 'Coder mieux';

// Retirer un élément
taches.remove('Tester'); // Retire la première occurrence
taches.removeAt(0); // Retire par index
taches.removeLast(); // Retire le dernier

// Retirer selon condition
taches.removeWhere((tache) => tache.contains('Dé'));

// Vider complètement
taches.clear();

Chercher des trucs

List<int> nombres = [5, 2, 8, 2, 9, 3, 8];

// Vérifier si c'est là
bool contient = nombres.contains(8); // true

// Trouver à quel endroit
int index = nombres.indexOf(8); // 2 (première occurrence)
int dernierIndex = nombres.lastIndexOf(8); // 6

// Index avec condition perso
int indexCondition = nombres.indexWhere((n) => n > 7); // 2 (le 8)

// Vérifier si tous/certains respectent une condition
bool tousPositifs = nombres.every((n) => n > 0); // true
bool certains = nombres.any((n) => n > 10); // false

Ces méthodes sont sympas mais attention à la perf. contains() et indexOf() parcourent la liste au pire. Si tu cherches beaucoup, un Set ou une Map c'est plus rapide.

Transformer des listes : map, where, reduce

C'est ici que ça devient vraiment cool. Au lieu d'écrire des boucles for partout, tu peux utiliser des méthodes qui rendent ton code plus lisible et concis.

map : Transformer chaque élément

map() applique une fonction à chaque élément et te retourne une nouvelle collection.

List<int> nombres = [1, 2, 3, 4, 5];

// Doubler chaque nombre
List<int> doubles = nombres.map((n) => n * 2).toList();
print(doubles); // [2, 4, 6, 8, 10]

// Transformer vers un autre type
List<String> textes = nombres.map((n) => 'Numéro $n').toList();
print(textes); // ['Numéro 1', 'Numéro 2', ...]

Important : map() te retourne un Iterable, pas une List. Tu dois appeler .toList() pour avoir une vraie liste.

Exemple concret : Transformer des données d'API

// Données brutes de l'API
List<Map<String, dynamic>> apiData = [
  {'id': 1, 'name': 'Alice', 'age': 25},
  {'id': 2, 'name': 'Bob', 'age': 30},
  {'id': 3, 'name': 'Charlie', 'age': 35},
];

// Créer une classe Utilisateur
class Utilisateur {
  final int id;
  final String nom;
  final int age;

  Utilisateur({required this.id, required this.nom, required this.age});

  @override
  String toString() => 'Utilisateur(id: $id, nom: $nom, age: $age)';
}

// Transformer en objets Utilisateur
List<Utilisateur> utilisateurs = apiData.map((json) {
  return Utilisateur(
    id: json['id'],
    nom: json['name'],
    age: json['age'],
  );
}).toList();

print(utilisateurs);
// [Utilisateur(id: 1, nom: Alice, age: 25), ...]

// Extraire juste les noms
List<String> noms = utilisateurs.map((u) => u.nom).toList();
print(noms); // ['Alice', 'Bob', 'Charlie']

where : Filtrer les éléments

where() te garde seulement les éléments qui respectent ta condition.

List<int> nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Garder seulement les pairs
List<int> pairs = nombres.where((n) => n % 2 == 0).toList();
print(pairs); // [2, 4, 6, 8, 10]

// Garder les nombres > 5
List<int> grands = nombres.where((n) => n > 5).toList();
print(grands); // [6, 7, 8, 9, 10]

Exemple concret : Filtrer des produits

class Produit {
  final String nom;
  final double prix;
  final bool enStock;

  Produit(this.nom, this.prix, this.enStock);
}

List<Produit> produits = [
  Produit('Laptop', 999.99, true),
  Produit('Souris', 25.50, true),
  Produit('Clavier', 75.00, false),
  Produit('Écran', 299.99, true),
  Produit('Webcam', 89.99, false),
];

// Produits en stock
List<Produit> disponibles = produits
  .where((p) => p.enStock)
  .toList();

// Produits en stock ET moins de 100€
List<Produit> abordables = produits
  .where((p) => p.enStock && p.prix < 100)
  .toList();

print('${abordables.length} produits cool et pas chers');
// 1 produits cool et pas chers (Souris)

Combiner map et where

Tu peux enchaîner les méthodes pour créer des pipelines super puissants.

List<int> nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Filtrer puis transformer
List<int> resultat = nombres
  .where((n) => n % 2 == 0)  // Garder les pairs
  .map((n) => n * n)          // Mettre au carré
  .toList();

print(resultat); // [4, 16, 36, 64, 100]

// Attention à l'ordre !
// where puis map = plus efficace (moins de choses à transformer)
// map puis where = moins efficace (transforme tout puis filtre)

Exemple concret : Préparer des données pour l'UI

class Utilisateur {
  final int id;
  final String nom;
  final bool actif;
  final int age;

  Utilisateur(this.id, this.nom, this.actif, this.age);
}

List<Utilisateur> users = [
  Utilisateur(1, 'alice', true, 25),
  Utilisateur(2, 'bob', false, 30),
  Utilisateur(3, 'charlie', true, 35),
  Utilisateur(4, 'david', true, 28),
];

// Pour l'écran, on veut :
// 1. Seulement les utilisateurs actifs
// 2. Nom en majuscules
// 3. Format "NOM (âge ans)"

List<String> affichage = users
  .where((u) => u.actif)
  .map((u) => '${u.nom.toUpperCase()} (${u.age} ans)')
  .toList();

print(affichage);
// ['ALICE (25 ans)', 'CHARLIE (35 ans)', 'DAVID (28 ans)']

reduce et fold : Agréger tout

reduce() et fold() c'est pour quand tu veux combiner tous les éléments en une seule valeur.

List<int> nombres = [1, 2, 3, 4, 5];

// reduce : combine les éléments deux par deux
int somme = nombres.reduce((total, element) => total + element);
print(somme); // 15

int produit = nombres.reduce((prod, element) => prod * element);
print(produit); // 120

// fold : comme reduce mais avec valeur de départ
int sommeAvecInitiale = nombres.fold(0, (total, element) => total + element);
print(sommeAvecInitiale); // 15

// fold est plus flexible
String concatene = nombres.fold('', (texte, n) => '$texte$n');
print(concatene); // '12345'

Différence clé : reduce() a besoin d'au moins un élément et retourne le même type. fold() peut gérer une liste vide et peut retourner un type différent.

Exemple concret : Calculer un total de panier

class Article {
  final String nom;
  final double prix;
  final int quantite;

  Article(this.nom, this.prix, this.quantite);

  double get sousTotal => prix * quantite;
}

List<Article> panier = [
  Article('Laptop', 999.99, 1),
  Article('Souris', 25.50, 2),
  Article('Clavier', 75.00, 1),
];

// Calculer le total
double total = panier.fold(0.0, (somme, article) => somme + article.sousTotal);

print('Total du panier: ${total.toStringAsFixed(2)}€');
// Total du panier: 1125.99€

// Compter le nombre d'articles
int nombreArticles = panier.fold(0, (total, article) => total + article.quantite);
print('$nombreArticles articles dans le panier');
// 4 articles dans le panier

Set : garantir l'unicité

Passons maintenant aux Set. Leur super-pouvoir ? Garantir que chaque élément est unique.

Créer et manipuler des Sets

// Créer un set vide
Set<String> tags = {};
// Attention : {} seul crée une Map, pas un Set !
// Utilise <String>{} pour être explicite

// Créer avec des valeurs
Set<int> nombres = {1, 2, 3, 4, 5};

// À partir d'une liste (élimine les doublons)
List<int> avecDoublons = [1, 2, 2, 3, 3, 3, 4, 5, 5];
Set<int> uniques = avecDoublons.toSet();
print(uniques); // {1, 2, 3, 4, 5}

// Ajouter des éléments
tags.add('dart');
tags.add('flutter');
tags.add('dart'); // Ignoré car déjà là
print(tags.length); // 2

// Ajouter plusieurs
tags.addAll(['mobile', 'développement']);

Opérations d'ensemble

C'est là que les Sets sont vraiment malades. Tu peux faire des opérations mathématiques d'ensemble.

Set<int> a = {1, 2, 3, 4, 5};
Set<int> b = {4, 5, 6, 7, 8};

// Union : tous les éléments des deux
Set<int> union = a.union(b);
print(union); // {1, 2, 3, 4, 5, 6, 7, 8}

// Intersection : que ce qui est dans les deux
Set<int> intersection = a.intersection(b);
print(intersection); // {4, 5}

// Différence : ce qui est dans A mais pas B
Set<int> difference = a.difference(b);
print(difference); // {1, 2, 3}

Exemple concret : Gestion de permissions

class Utilisateur {
  final String nom;
  final Set<String> permissions;

  Utilisateur(this.nom, this.permissions);

  bool peutFaire(String action) {
    return permissions.contains(action);
  }

  void ajouterPermission(String permission) {
    permissions.add(permission);
  }
}

// Définir des rôles avec leurs permissions
final adminPermissions = {
  'lire',
  'écrire',
  'supprimer',
  'gérer_utilisateurs',
};

final editorPermissions = {
  'lire',
  'écrire',
};

final readerPermissions = {
  'lire',
};

// Créer des utilisateurs
var admin = Utilisateur('Alice', {...adminPermissions});
var editor = Utilisateur('Bob', {...editorPermissions});

// Vérifier les permissions
print(admin.peutFaire('supprimer')); // true
print(editor.peutFaire('supprimer')); // false

// Trouver les permissions communes
Set<String> communesAdminEditor = 
  adminPermissions.intersection(editorPermissions);
print('Permissions communes: $communesAdminEditor');
// {lire, écrire}

// Permissions exclusives à admin
Set<String> exclusivesAdmin = 
  adminPermissions.difference(editorPermissions);
print('Exclusives admin: $exclusivesAdmin');
// {supprimer, gérer_utilisateurs}

Éliminer les doublons facilement

C'est un cas d'usage super courant : tu as une liste avec des doublons et tu veux les nettoyer.

List<String> messagesAvecDoublons = [
  'Bonjour',
  'Comment ça va ?',
  'Bonjour',
  'Très bien merci',
  'Comment ça va ?',
  'Super !',
];

// Méthode 1 : Convertir en Set puis en List
List<String> uniques = messagesAvecDoublons.toSet().toList();
print(uniques); // ['Bonjour', 'Comment ça va ?', 'Très bien merci', 'Super !']

// Méthode 2 : Garder l'ordre avec LinkedHashSet
Set<String> set = LinkedHashSet.from(messagesAvecDoublons);
List<String> uniquesOrdonnees = set.toList();

Performance : La conversion toSet() est très rapide. Pour vérifier si un élément existe, un Set c'est O(1) alors qu'une List c'est O(n).

Map : la puissance des paires clé-valeur

Les Map c'est extraordinairement utile. Elles permettent d'associer des clés uniques à des valeurs.

Créer et accéder aux Maps

// Map vide
Map<String, int> scores = {};

// Map avec des valeurs
Map<String, String> traductions = {
  'hello': 'bonjour',
  'goodbye': 'au revoir',
  'thanks': 'merci',
};

// Accéder à une valeur
String? bonjour = traductions['hello'];
print(bonjour); // 'bonjour'

// Valeur par défaut si clé inexistante
String merci = traductions['thanks'] ?? 'valeur par défaut';

// Modifier une valeur
traductions['hello'] = 'salut';

// Ajouter une nouvelle paire
traductions['yes'] = 'oui';

// Supprimer une clé
traductions.remove('goodbye');

Méthodes utiles

Map<String, int> ages = {
  'Alice': 25,
  'Bob': 30,
  'Charlie': 35,
};

// Vérifier l'existence d'une clé
bool contientAlice = ages.containsKey('Alice'); // true
bool contient25 = ages.containsValue(25); // true

// Obtenir toutes les clés ou valeurs
List<String> noms = ages.keys.toList();
List<int> agesListe = ages.values.toList();

// Nombre d'éléments
int taille = ages.length;

// Vider la map
ages.clear();

Itérer sur une Map

Il y a plusieurs façons de faire, selon ce que tu veux.

Map<String, int> scores = {
  'Alice': 95,
  'Bob': 87,
  'Charlie': 92,
};

// Méthode 1 : forEach
scores.forEach((nom, score) {
  print('$nom a obtenu $score points');
});

// Méthode 2 : for-in sur les entries
for (var entry in scores.entries) {
  print('${entry.key}: ${entry.value}');
}

// Méthode 3 : for-in sur les clés
for (var nom in scores.keys) {
  print('$nom: ${scores[nom]}');
}

// Transformer en liste
List<String> resultats = scores.entries
  .map((e) => '${e.key} a eu ${e.value}/100')
  .toList();

Grouper des données avec Map

Un pattern super utile : grouper des éléments par catégorie.

class Produit {
  final String nom;
  final String categorie;
  final double prix;

  Produit(this.nom, this.categorie, this.prix);
}

List<Produit> produits = [
  Produit('Laptop', 'Électronique', 999.99),
  Produit('Souris', 'Électronique', 25.50),
  Produit('Bureau', 'Mobilier', 299.99),
  Produit('Chaise', 'Mobilier', 149.99),
  Produit('Clavier', 'Électronique', 75.00),
];

// Grouper par catégorie
Map<String, List<Produit>> parCategorie = {};

for (var produit in produits) {
  // Si la catégorie n'existe pas, créer une liste vide
  parCategorie.putIfAbsent(produit.categorie, () => []);
  parCategorie[produit.categorie]!.add(produit);
}

// Afficher
parCategorie.forEach((categorie, produits) {
  print('\n$categorie (${produits.length} produits):');
  for (var p in produits) {
    print('  - ${p.nom}: ${p.prix}€');
  }
});

// Output:
// Électronique (3 produits):
//   - Laptop: 999.99€
//   - Souris: 25.50€
//   - Clavier: 75.0€
// 
// Mobilier (2 produits):
//   - Bureau: 299.99€
//   - Chaise: 149.99€

Compter les occurrences

Un autre pattern classique : compter combien de fois chaque élément apparaît.

List<String> votes = [
  'Rouge', 'Bleu', 'Rouge', 'Vert',
  'Rouge', 'Bleu', 'Jaune', 'Rouge',
  'Vert', 'Rouge', 'Bleu', 'Rouge',
];

// Compter les votes
Map<String, int> compteur = {};

for (var vote in votes) {
  compteur[vote] = (compteur[vote] ?? 0) + 1;
}

print(compteur);
// {Rouge: 6, Bleu: 3, Vert: 2, Jaune: 1}

// Trouver le gagnant
String gagnant = '';
int maxVotes = 0;

compteur.forEach((couleur, votes) {
  if (votes > maxVotes) {
    maxVotes = votes;
    gagnant = couleur;
  }
});

print('Gagnant: $gagnant avec $maxVotes votes');
// Gagnant: Rouge avec 6 votes

Méthodes avancées et astuces

Maintenant qu'on a vu les bases, explorons des techniques plus avancées.

expand : Aplatir des listes imbriquées

expand() transforme une liste de listes en une seule liste plate.

List<List<int>> listes = [
  [1, 2, 3],
  [4, 5],
  [6, 7, 8, 9],
];

// Aplatir en une seule liste
List<int> aplatie = listes.expand((liste) => liste).toList();
print(aplatie); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// Exemple pratique : extraire tous les tags de plusieurs articles
class Article {
  final String titre;
  final List<String> tags;

  Article(this.titre, this.tags);
}

List<Article> articles = [
  Article('Article 1', ['dart', 'flutter']),
  Article('Article 2', ['mobile', 'ios']),
  Article('Article 3', ['dart', 'backend']),
];

// Obtenir tous les tags uniques
Set<String> tousTags = articles
  .expand((article) => article.tags)
  .toSet();

print(tousTags); // {dart, flutter, mobile, ios, backend}

take, skip et leurs variantes

Ces méthodes c'est cool quand tu veux prendre ou sauter des éléments selon certains critères.

List<int> nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// take : prendre les N premiers
List<int> premiers = nombres.take(3).toList();
print(premiers); // [1, 2, 3]

// skip : sauter les N premiers
List<int> apres = nombres.skip(7).toList();
print(apres); // [8, 9, 10]

// takeWhile : prendre tant que c'est vrai
List<int> petits = nombres.takeWhile((n) => n < 6).toList();
print(petits); // [1, 2, 3, 4, 5]

// skipWhile : sauter tant que c'est vrai
List<int> grands = nombres.skipWhile((n) => n < 6).toList();
print(grands); // [6, 7, 8, 9, 10]

Exemple d'utilisation : Pagination

class Pagination {
  static List<T> getPage<T>(List<T> items, int page, int itemsParPage) {
    return items
      .skip(page * itemsParPage)
      .take(itemsParPage)
      .toList();
  }
}

List<String> articles = [
  'Article 1', 'Article 2', 'Article 3', 'Article 4',
  'Article 5', 'Article 6', 'Article 7', 'Article 8',
  'Article 9', 'Article 10',
];

// Page 0 (3 articles par page)
print(Pagination.getPage(articles, 0, 3));
// [Article 1, Article 2, Article 3]

// Page 1
print(Pagination.getPage(articles, 1, 3));
// [Article 4, Article 5, Article 6]

firstWhere et singleWhere

Ces méthodes c'est pour trouver des éléments selon une condition.

List<int> nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// firstWhere : premier élément qui respecte la condition
int premierPair = nombres.firstWhere((n) => n % 2 == 0);
print(premierPair); // 2

// Avec valeur par défaut (orElse)
int premierGrand = nombres.firstWhere(
  (n) => n > 20,
  orElse: () => -1,
);
print(premierGrand); // -1 (aucun élément > 20)

// lastWhere : dernier élément qui respecte la condition
int dernierPair = nombres.lastWhere((n) => n % 2 == 0);
print(dernierPair); // 10

// singleWhere : s'assure qu'il y a exactement un élément
List<int> nombres2 = [1, 5, 3];
int cinq = nombres2.singleWhere((n) => n == 5);
print(cinq); // 5

// Si 0 ou plus de 1 élément → Exception
// nombres2.singleWhere((n) => n > 2); // ERREUR : 2 éléments (5 et 3)

Performance : choisir la bonne structure

Le choix entre List, Set et Map n'est pas qu'une question de préférence - ça affecte les performances.

Complexité des opérations

Opération List Set Map
Ajout (fin) O(1) O(1) O(1)
Chercher O(n) O(1) O(1)
Accès par index O(1) - -
Accès par clé - - O(1)
Suppression O(n) O(1) O(1)

Que signifient ces notations ?

  • O(1) : Temps constant, super rapide peu importe la taille

  • O(n) : Temps proportionnel à la taille, plus lent si c'est big

En pratique :

  • Si tu cherches beaucoup avec contains() → utilise un Set

  • Si t'as des paires clé-valeur et tu cherches par clé → utilise une Map

  • Si tu as besoin d'ordre et d'accès par index → utilise une List

Exemple de comparaison

import 'dart:math';

void comparerPerformances() {
  final random = Random();

  // Créer 10000 nombres aléatoires
  List<int> nombresListe = List.generate(10000, (_) => random.nextInt(1000));
  Set<int> nombresSet = nombresListe.toSet();

  // Test : chercher 1000 fois si un nombre existe
  final debut1 = DateTime.now();
  for (int i = 0; i < 1000; i++) {
    nombresListe.contains(500);
  }
  final duree1 = DateTime.now().difference(debut1);

  final debut2 = DateTime.now();
  for (int i = 0; i < 1000; i++) {
    nombresSet.contains(500);
  }
  final duree2 = DateTime.now().difference(debut2);

  print('List.contains: ${duree1.inMicroseconds}µs');
  print('Set.contains: ${duree2.inMicroseconds}µs');
  print('Set est ${duree1.inMicroseconds / duree2.inMicroseconds}x plus rapide');
}

Sur ma machine, Set est genre 100x plus rapide pour les recherches !

Bonnes pratiques

Terminons avec des conseils pour utiliser les collections comme tu dois.

1. Utilise const pour les collections immuables

// ❌ Mauvais : collection recréée à chaque fois
List<String> getCouleurs() {
  return ['Rouge', 'Vert', 'Bleu'];
}

// ✅ Bon : collection constante, créée une seule fois
const List<String> COULEURS = ['Rouge', 'Vert', 'Bleu'];

2. Préfère les méthodes fonctionnelles aux boucles

// ❌ Verbeux
List<int> doubles = [];
for (var n in nombres) {
  doubles.add(n * 2);
}

// ✅ Concis et lisible
List<int> doubles = nombres.map((n) => n * 2).toList();

3. Utilise l'opérateur spread pour combiner

List<int> a = [1, 2, 3];
List<int> b = [4, 5, 6];

// ❌ Mauvais
List<int> combine = [];
combine.addAll(a);
combine.addAll(b);

// ✅ Bon
List<int> combine = [...a, ...b];

// Avec condition
bool includeB = true;
List<int> combine2 = [
  ...a,
  if (includeB) ...b,
];

4. Gère les collections nullables proprement

List<String>? maybeListe;

// ❌ Peut crasher
int longueur = maybeListe.length;

// ✅ Sécurisé
int longueur = maybeListe?.length ?? 0;

// Itérer en toute sécurité
for (var item in maybeListe ?? []) {
  print(item);
}

Conclusion

Les collections c'est vraiment le coeur de tout ce que tu codes en Dart. Les maîtriser te permet d'écrire du code plus lisible, plus rapide et plus court.

Points clés à retenir :

  • List : Ordre, accès par index, peut avoir des doublons

  • Set : Unicité garantie, recherche super rapide, opérations d'ensemble

  • Map : Paires clé-valeur, lookup rapide

Méthodes essentielles :

  • map() pour transformer

  • where() pour filtrer

  • reduce() et fold() pour agréger

  • expand() pour aplatir

  • firstWhere() pour chercher

Performance :

  • Set et Map : O(1) pour recherche/ajout/suppression

  • List : O(1) pour accès par index, O(n) pour recherche

Maintenant, va écrire du code plus cool avec ces méthodes !


Pour approfondir :

T'as des questions sur les collections ? Balance-les dans les commentaires !


Articles connexes:

  1. La programmation orientée objet en Dart

  2. Les fondamentaux du langage de Dart

Commentaires (1)

Laisser un commentaire