lundi 1 septembre 2025

Dictionary



✅ Questions & Réponses sur Dictionary en C#

1. Qu’est-ce qu’un Dictionary en C# ?

  • Réponse :
    Un Dictionary<TKey, TValue> est une collection générique de paires clé/valeur dans laquelle chaque clé est unique. Il permet un accès rapide aux valeurs en fonction de leur clé, grâce à un mécanisme de hachage.


2. Quelle est la complexité temporelle des opérations dans un Dictionary ?

  • Réponse :
    En moyenne :

  • Ajout (Add) : O(1)

  • Recherche (ContainsKey, indexeur dict[key]) : O(1)

  • Suppression (Remove) : O(1)

  • Dans le pire des cas (collision excessive) : O(n).


3. Quelle est la différence entre Dictionary et Hashtable ?

  • Réponse :

    • Dictionary<TKey, TValue> est générique (type-safe).

    • Hashtable est non générique (stocke des objets, nécessite des cast).

    • Dictionary est généralement plus performant car il évite le boxing/unboxing.

    • Dictionary préserve l’ordre d’insertion à partir de .NET Core 3.0.


4. Comment vérifier si une clé existe dans un Dictionary ?

  • Réponse :

if (dict.ContainsKey("clé")) { ... }

ou

if (dict.TryGetValue("clé", out var value)) { ... }

👉 TryGetValue est préférable car il évite une double recherche.


5. Que se passe-t-il si on ajoute une clé déjà existante ?

  • Réponse :

    • Avec dict.Add(key, value)exception ArgumentException.

    • Avec l’indexeur dict[key] = value → la valeur est mise à jour.


6. Quelle est la différence entre TryGetValue et l’indexeur dict[key] ?

  • Réponse :

    • dict[key] lève une exception KeyNotFoundException si la clé n’existe pas.

    • TryGetValue retourne false si la clé n’existe pas, et affecte out value à la valeur par défaut du type.


7. Comment parcourir un Dictionary ?

  • Réponse :

foreach (var kvp in dict)
{
    Console.WriteLine($"{kvp.Key} : {kvp.Value}");
}

Ou uniquement les clés :

foreach (var key in dict.Keys) { ... }

Ou uniquement les valeurs :

foreach (var value in dict.Values) { ... }

8. Comment supprimer un élément dans un Dictionary ?

  • Réponse :

dict.Remove("clé");

Retourne true si la clé existait et a été supprimée.


9. Peut-on utiliser null comme clé dans un Dictionary ?

  • Réponse :
    Non, Dictionary ne permet pas null comme clé (sauf si TKey est nullable et que vous utilisez un comparer personnalisé).
    En revanche, les valeurs peuvent être null.


10. Comment personnaliser la comparaison des clés ?

  • Réponse :
    En passant un IEqualityComparer<TKey> au constructeur du Dictionary :

var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

👉 Ici, les clés "Test" et "test" seront considérées comme identiques.


11. Quelle est la différence entre Dictionary et ConcurrentDictionary ?

  • Réponse :

    • Dictionary n’est pas thread-safe → pas sûr pour un accès concurrent.

    • ConcurrentDictionary est conçu pour les environnements multithreads (lock interne optimisé).

    • Exemple :

var concurrentDict = new ConcurrentDictionary<int, string>();
concurrentDict.TryAdd(1, "Hello");

12. Comment convertir un Dictionary en List ou en LINQ ?

  • Réponse :

var list = dict.ToList(); // List<KeyValuePair<TKey, TValue>>

Ou filtrer avec LINQ :

var filtered = dict.Where(kvp => kvp.Value.StartsWith("A"))
                   .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

13. Peut-on contrôler la capacité d’un Dictionary ?

  • Réponse :
    Oui, avec le constructeur :

var dict = new Dictionary<int, string>(capacity: 100);

Cela améliore les performances si on connaît à l’avance la taille.


14. Qu’est-ce qu’une collision dans un Dictionary ?

  • Réponse :
    Une collision se produit lorsque deux clés différentes génèrent le même code de hachage.
    Le Dictionary les stocke alors dans une structure interne (liste chaînée ou buckets) et les distingue avec Equals.


15. Comment fusionner deux Dictionary ?

  • Réponse :

foreach (var kvp in dict2)
    dict1[kvp.Key] = kvp.Value; // écrase si la clé existe

Ou en LINQ :

var merged = dict1.Concat(dict2)
                  .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);



⚡️ Pièges fréquents sur Dictionary en C#

1. Que se passe-t-il si on fait dict["cléInexistante"] ?

  • Piège : Beaucoup pensent que ça retourne null.

  • Réponse : En réalité, ça lance une exception KeyNotFoundException.
    👉 La bonne pratique est d’utiliser TryGetValue.


2. Quelle est la différence entre dict.Add(k, v) et dict[k] = v ?

  • Piège : Certains pensent que c’est identique.

  • Réponse :

    • Add(k, v) lève une exception si la clé existe déjà.

    • dict[k] = v écrase simplement la valeur existante.


3. L’ordre des éléments est-il garanti dans un Dictionary ?

  • Piège : Avant .NET Core 3.0, l’ordre d’énumération n’était pas garanti.

  • Réponse : Depuis .NET Core 3.0 et .NET 5+, l’ordre d’insertion est préservé.
    👉 En entretien, il faut préciser "ça dépend de la version du framework".


4. Peut-on avoir deux clés différentes qui ont le même hash code ?

  • Piège : Beaucoup pensent que c’est impossible.

  • Réponse : Oui, c’est possible → c’est une collision.
    Le Dictionary utilise à la fois GetHashCode() et Equals() pour distinguer les clés.


5. Que se passe-t-il si la clé est null ?

  • Piège : Certains pensent que null est accepté.

  • Réponse : Non, Dictionary n’accepte pas null comme clé (sauf cas particulier avec Nullable<T> et un comparer personnalisé).
    👉 Par contre, les valeurs peuvent être null.


6. Un Dictionary est-il thread-safe ?

  • Piège : Beaucoup pensent que oui car c’est une collection "haut niveau".

  • Réponse : Non, Dictionary n’est pas thread-safe.
    Pour les scénarios multithread, il faut utiliser ConcurrentDictionary.


7. Que se passe-t-il si on modifie un Dictionary pendant qu’on l’énumère ?

  • Piège : Certains pensent que ça met juste à jour l’énumération.

  • Réponse : Ça lance une exception InvalidOperationException.
    👉 Solution : utiliser ToList() avant, ou ConcurrentDictionary.


8. Quelle est la différence entre ContainsKey et ContainsValue ?

  • Piège : Certains pensent que les deux sont O(1).

  • Réponse :

    • ContainsKey est O(1) en moyenne.

    • ContainsValue est O(n) car il doit parcourir toutes les valeurs.


9. Un Dictionary peut-il contenir des doublons ?

  • Piège : Certains répondent "oui" en pensant aux valeurs.

  • Réponse :

    • Doublons de clés → impossible.

    • Doublons de valeurs → oui, autorisé.


10. Différence entre dict.Keys et dict.Values ?

  • Piège : Certains pensent que ce sont des copies.

  • Réponse : Ce sont des vues dynamiques (pas des copies).
    Si on modifie le dictionnaire, dict.Keys et dict.Values se mettent à jour automatiquement.


11. Que se passe-t-il si la méthode GetHashCode() d’une clé change après insertion dans un Dictionary ?

  • Piège : Question avancée qui piège même des seniors.

  • Réponse : Si la clé est mutable et que son hash change, elle devient intraçable → impossible de la retrouver.
    👉 Toujours utiliser des clés immuables (ex. string, int).


12. Peut-on comparer deux Dictionary directement avec == ou .Equals() ?

  • Piège : Certains pensent que ça compare les éléments.

  • Réponse : Non, ça compare uniquement les références.
    👉 Pour comparer le contenu :

dict1.OrderBy(kvp => kvp.Key)
     .SequenceEqual(dict2.OrderBy(kvp => kvp.Key));

13. Que vaut dict.Count si on supprime une clé inexistante ?

  • Piège : Certains pensent qu’une exception est levée.

  • Réponse : Remove(key) retourne false, et Count reste inchangé.


14. Un Dictionary est-il toujours plus rapide qu’une List pour chercher un élément ?

  • Piège : Réponse automatique "oui".

  • Réponse : En général oui (O(1) vs O(n)), mais :

    • Pour des petites collections (< 10 éléments), une List peut être plus rapide à cause du surcoût du hachage.

    • Les performances dépendent de la taille et du scénario.




🔎 Comment un Dictionary gère une collision ?

Un Dictionary est basé sur un tableau de buckets (seaux).
Quand on insère une clé :

  1. Le hash code de la clé (GetHashCode()) est calculé.

  2. Ce hash est transformé en un index de bucket (via modulo sur la capacité).

  3. Si deux clés différentes donnent le même index, c’est une collision.

  4. Le Dictionary stocke alors les deux entrées dans une structure de liste chaînée interne (ou un tableau de "entries").

  5. Lors de l’accès, il compare d’abord les hash codes, puis utilise Equals() pour distinguer les clés.


✅ Exemple de collision

class BadKey
{
    public string Value { get; set; }

    // Mauvaise implémentation : toutes les clés ont le même hash !
    public override int GetHashCode() => 42;

    public override bool Equals(object obj)
        => obj is BadKey other && Value == other.Value;
}

var dict = new Dictionary<BadKey, string>();
dict[new BadKey { Value = "A" }] = "Alpha";
dict[new BadKey { Value = "B" }] = "Bravo";

Console.WriteLine(dict.Count); // 2 (collision interne gérée)

👉 Ici, toutes les clés tombent dans le même bucket, mais grâce à Equals(), le Dictionary fait la distinction.


🚀 Bonnes pratiques pour éviter / bien gérer les collisions

  1. Toujours surcharger GetHashCode() et Equals() de manière cohérente :

    • Deux objets égaux (Equals == true) doivent avoir le même hash code.

    • Mais deux objets différents peuvent avoir le même hash (collision tolérée).

  2. Utiliser des clés immuables (comme string, int, Guid) → évite que le hash change après insertion.

  3. Utiliser un IEqualityComparer<TKey> personnalisé si nécessaire :
    Exemple avec un comparer insensible à la casse :

    var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    dict["Test"] = "Hello";
    Console.WriteLine(dict.ContainsKey("test")); // True
    
  4. Éviter les hash codes constants ou mal répartis → sinon toutes les clés se retrouvent dans le même bucket, et les performances tombent de O(1) à O(n).


⚡ Question piège en entretien

👉 "Que se passe-t-il si toutes les clés d’un Dictionary ont le même hash code ?"

  • Réponse attendue :
    Le Dictionary continue de fonctionner correctement (grâce à Equals), mais les performances chutent car toutes les recherches se font dans une liste chaînée interne → donc complexité O(n) au lieu de O(1).


Return

Aucun commentaire:

Enregistrer un commentaire

Dictionary Lượt xem: