Created
October 9, 2025 08:47
-
-
Save evaisse/7c04ceffc914a5177457526e51409e9c to your computer and use it in GitHub Desktop.
Dart & comparaison , égalité logique (operator ==) , hashCode , compareTo ...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'dart:core'; | |
| /// Démo d’égalité, hashCode et comparaison d’entités. | |
| /// | |
| /// Points clés: | |
| /// - L’égalité logique (operator ==) est basée sur l’ID en minuscules. | |
| /// - Le hashCode est cohérent avec == (utilise le même critère). | |
| /// - compareTo (Comparable<MyEntity>) illustre un ordre de tri. | |
| /// | |
| /// Subtilités: | |
| /// - == et hashCode doivent être cohérents: si a == b alors a.hashCode == b.hashCode. | |
| /// - hashCode n’implique PAS l’égalité (collisions possibles). | |
| /// - compareTo définit un ordre de tri. S’il est cohérent avec ==, alors compareTo(a,b) == 0 <=> a == b. | |
| /// Ce n’est pas obligatoire, mais fortement recommandé pour éviter des comportements surprenants dans des structures ordonnées. | |
| class MyEntity implements Comparable<MyEntity> { | |
| final String id; | |
| final String name; | |
| const MyEntity({required this.id, required this.name}); | |
| /// Égalité logique basée uniquement sur l’ID, insensible à la casse. | |
| /// | |
| /// Bonnes pratiques: | |
| /// - Vérifier identical(this, other) pour le fast path. | |
| /// - Vérifier le type. | |
| /// - Utiliser la même logique que hashCode. | |
| @override | |
| bool operator ==(Object other) { | |
| if (identical(this, other)) return true; | |
| return other is MyEntity && | |
| runtimeType == other.runtimeType && | |
| id.toLowerCase() == other.id.toLowerCase(); | |
| } | |
| /// hashCode doit utiliser le même critère que ==. | |
| /// | |
| /// ATTENTION: Si tu modifies la logique d’égalité, mets à jour hashCode. | |
| @override | |
| int get hashCode => id.toLowerCase().hashCode; | |
| /// Ordre naturel de tri. | |
| /// | |
| /// Ici, on choisit un ordre cohérent avec ==: | |
| /// - On compare d’abord l’ID insensible à la casse. | |
| /// - Si les IDs sont égaux (case-insensitive), on compare le name (insensible à la casse) | |
| /// uniquement pour départager dans l’ordre, PAS pour l’égalité. | |
| /// | |
| /// Conséquence: | |
| /// - Si compareTo retourne 0, les IDs (en minuscules) sont égaux, donc == est true. | |
| /// - Deux entités avec même ID mais noms différents seront égales (== true), mais compareTo | |
| /// peut quand même retourner 0 (si on le décide) ou départager via name. | |
| /// | |
| /// Pour stricte cohérence == <=> compareTo == 0, il faut que compareTo ignore aussi name. | |
| /// Ci-dessous, nous faisons ce choix pour la stricte cohérence: compareTo ne tient compte que de l’ID en minuscules. | |
| @override | |
| int compareTo(MyEntity other) { | |
| final a = id.toLowerCase(); | |
| final b = other.id.toLowerCase(); | |
| return a.compareTo(b); | |
| } | |
| @override | |
| String toString() => 'MyEntity(id: $id, name: $name)'; | |
| } | |
| void main() { | |
| print('--- Démonstration: ==, hashCode, compareTo ---'); | |
| // Création d’entités avec IDs équivalents logiquement (insensible à la casse) | |
| final MyEntity entity1 = const MyEntity(id: 'ABC123', name: 'First Item'); | |
| final MyEntity entity2 = const MyEntity(id: 'abc123', name: 'Second Item'); | |
| final MyEntity entity3 = const MyEntity(id: 'Abc123', name: 'Third Item'); | |
| // Entité avec ID différent | |
| final MyEntity entity4 = const MyEntity(id: 'DEF456', name: 'Another Item'); | |
| // Même ID que entity2 mais nom différent | |
| final MyEntity entity5 = const MyEntity(id: 'abc123', name: 'Renamed Item'); | |
| print('\nEntities created:'); | |
| print('entity1: $entity1'); | |
| print('entity2: $entity2'); | |
| print('entity3: $entity3'); | |
| print('entity4: $entity4'); | |
| print('entity5: $entity5'); | |
| print('\n--- Égalité (operator ==) ---'); | |
| // Même ID logique -> true | |
| print('entity1 == entity2: ${entity1 == entity2}'); // true | |
| print('entity1 == entity3: ${entity1 == entity3}'); // true | |
| print('entity2 == entity3: ${entity2 == entity3}'); // true | |
| print('entity2 == entity5: ${entity2 == entity5}'); // true (name ignoré) | |
| // ID différent -> false | |
| print('entity1 == entity4: ${entity1 == entity4}'); // false | |
| print('\n--- hashCode ---'); | |
| // hashCode égal pour objets égaux | |
| print('entity1 hashCode: ${entity1.hashCode}'); | |
| print('entity2 hashCode: ${entity2.hashCode}'); | |
| print('entity3 hashCode: ${entity3.hashCode}'); | |
| print('entity5 hashCode: ${entity5.hashCode}'); // même que entity1..3 | |
| // hashCode probablement différent pour entité non égale | |
| print('entity4 hashCode: ${entity4.hashCode}'); | |
| print('\nVérifications hashCode:'); | |
| print('entity1.hashCode == entity2.hashCode: ${entity1.hashCode == entity2.hashCode}'); // true | |
| print('entity1.hashCode == entity3.hashCode: ${entity1.hashCode == entity3.hashCode}'); // true | |
| print('entity2.hashCode == entity5.hashCode: ${entity2.hashCode == entity5.hashCode}'); // true | |
| print('entity1.hashCode == entity4.hashCode: ${entity1.hashCode == entity4.hashCode}'); // false (très probable) | |
| print('\n--- compareTo (Comparable) ---'); | |
| // Rappels: | |
| // - compareTo < 0 : this < other | |
| // - compareTo = 0 : "égaux" pour l’ordre (et ici aussi == true car on a lié l’ordre à l’égalité d’ID) | |
| // - compareTo > 0 : this > other | |
| // Même ID logique -> 0 | |
| print('entity1.compareTo(entity2): ${entity1.compareTo(entity2)}'); // 0 | |
| print('entity2.compareTo(entity3): ${entity2.compareTo(entity3)}'); // 0 | |
| print('entity1.compareTo(entity5): ${entity1.compareTo(entity5)}'); // 0 | |
| // IDs différents -> ordre alphabétique insensible à la casse sur l’ID | |
| print('entity1.compareTo(entity4): ${entity1.compareTo(entity4)}'); // 'abc123' vs 'def456' -> < 0 | |
| print('\n--- Tri avec compareTo ---'); | |
| final list = <MyEntity>[entity4, entity3, entity1, entity5, entity2]; | |
| print('Avant tri: $list'); | |
| list.sort(); // utilise compareTo | |
| print('Après tri (par ID case-insensitive): $list'); | |
| print('\n--- Utilisation dans Set et Map (fondés sur == et hashCode) ---'); | |
| // Set: pas de doublons logiques (== true) si hashCode est cohérent | |
| final set = <MyEntity>{}; | |
| set.add(entity1); | |
| set.add(entity2); // considéré comme doublon (même ID logique) | |
| set.add(entity3); // doublon | |
| set.add(entity4); | |
| set.add(entity5); // doublon de entity1/2/3 | |
| print('Set (taille attendue 2: ABC123, DEF456): $set'); | |
| print('set.length: ${set.length}'); | |
| // Map: mêmes clés logiques se remplacent | |
| final map = <MyEntity, String>{}; | |
| map[entity1] = 'value for entity1'; | |
| map[entity2] = 'value for entity2 (overwrites entity1)'; // même clé logique | |
| map[entity3] = 'value for entity3 (overwrites again)'; | |
| map[entity4] = 'value for entity4'; | |
| map[entity5] = 'value for entity5 (overwrites again)'; | |
| print('Map keys: ${map.keys.toList()}'); | |
| print('Map values: ${map.values.toList()}'); | |
| print('map.length (attendu 2): ${map.length}'); | |
| print('\n--- Pièges et bonnes pratiques ---'); | |
| // 1) Incohérence entre == et hashCode: | |
| // Si tu changes == sans mettre à jour hashCode, les Set/Map auront des comportements erratiques. | |
| // 2) Collisions de hashCode: | |
| // Deux objets différents peuvent avoir le même hashCode (c’est permis). Ne jamais inférer l’égalité via hashCode. | |
| // 3) compareTo et ==: | |
| // Dans cet exemple, compareTo == 0 <=> == true (cohérents, car tous deux basés sur l’ID insensible à la casse). | |
| // Si compareTo prenait aussi le name, on pourrait avoir compareTo != 0 alors que == true, ce qui est parfois surprenant | |
| // pour certaines structures ordonnées. Choisis la cohérence explicitement selon tes besoins. | |
| // 4) Collections: | |
| // - Set/Map utilisent ==/hashCode, pas compareTo. | |
| // - sort()/SplayTree utilisent compareTo (ou un Comparator fourni). | |
| // 5) Performance: | |
| // - hashCode doit être rapide et stable (en fonction des champs immuables). | |
| // - compareTo peut être plus coûteux; optimiser si nécessaire. | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment