mercredi 8 octobre 2025

Q/A Collections



🔹 1. Quelle est la différence entre List et ArrayList ?

Critère List ArrayList
Namespace System.Collections.Generic System.Collections
Type de données Générique (type sûr) Non générique (type object)
Sécurité de type Compile-time (empêche les erreurs de cast) ✅ Runtime (requiert des cast) ❌
Performance Meilleure (pas de boxing/unboxing) Moins bonne (boxing des types valeur)
Exemple List<int> list = new List<int>(); ArrayList list = new ArrayList();

🧩 Exemple :

List<int> list = new List<int> { 1, 2, 3 };        // ✅ Type-sûr
ArrayList arr = new ArrayList { 1, "abc", 3.5 };   // ❌ Types mélangés

🧠 Conclusion :

List<T> est préférée car elle est fortement typée, performante et moderne.
ArrayList est obsolète depuis l’introduction des collections génériques.


🔹 2. Pourquoi Dictionary<TKey, TValue> est-il plus rapide que List<KeyValuePair<TKey, TValue>> pour la recherche ?

  • Dictionary<TKey, TValue> utilise une table de hachage (hash table) pour stocker les éléments.

    • La recherche (ContainsKey, TryGetValue) s’effectue en O(1) en moyenne.

  • List<KeyValuePair<TKey, TValue>> effectue une recherche séquentielle sur chaque élément.

    • La recherche est en O(n).

🧩 Exemple :

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "Alice");
dict.Add(2, "Bob");

dict.TryGetValue(2, out var name); // O(1)

List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();
list.Add(new KeyValuePair<int, string>(1, "Alice"));
list.Add(new KeyValuePair<int, string>(2, "Bob"));
var found = list.FirstOrDefault(x => x.Key == 2); // O(n)

🧠 Conclusion :

Dictionary est optimisé pour la recherche rapide grâce à son mécanisme de hachage interne.


🔹 3. Différence entre Queue et Stack

Collection Type Principe Méthodes principales
Queue File (FIFO) First In, First Out Enqueue(), Dequeue()
Stack Pile (LIFO) Last In, First Out Push(), Pop()

🧩 Exemple :

Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
Console.WriteLine(queue.Dequeue()); // A (FIFO)

Stack<string> stack = new Stack<string>();
stack.Push("A");
stack.Push("B");
Console.WriteLine(stack.Pop()); // B (LIFO)

🧠 Conclusion :

Queue<T> gère les éléments dans l’ordre d’arrivée (ex : files d’attente).
Stack<T> gère les éléments en ordre inverse (ex : historique de navigation).


🔹 4. Qu'est-ce qu’un LinkedList et quand l’utiliser ?

✅ Définition :

LinkedList<T> est une liste doublement chaînée, où chaque élément contient un lien vers le précédent et le suivant.

✅ Avantages :

  • Insertion et suppression rapides en O(1) à n’importe quelle position si le nœud est connu.

  • Pas de redimensionnement comme dans un tableau.

⚠️ Inconvénients :

  • Accès à un élément par index coûteux (O(n)).

  • Consomme plus de mémoire (pointeurs supplémentaires).

🧩 Exemple :

LinkedList<string> list = new LinkedList<string>();
list.AddLast("A");
list.AddLast("B");
list.AddFirst("Start");

🧠 Quand l’utiliser :

Quand on a beaucoup d’insertion/suppression au milieu de la collection,
mais peu d’accès aléatoire par index.


🔹 5. Quelle est la complexité de recherche dans un HashSet ?

  • HashSet<T> stocke les éléments uniques en utilisant un algorithme de hachage.

  • Complexité moyenne : O(1)

  • Pire cas (collisions extrêmes) : O(n)

🧩 Exemple :

HashSet<int> set = new HashSet<int> { 1, 2, 3 };
bool exists = set.Contains(2); // O(1)

🧠 Conclusion :

HashSet<T> est idéal pour tester rapidement la présence ou l’unicité d’un élément.


🔹 6. Qu'est-ce que SortedList<TKey, TValue> et en quoi diffère-t-il de SortedDictionary<TKey, TValue> ?

Critère SortedList<TKey, TValue> SortedDictionary<TKey, TValue>
Structure interne Deux tableaux triés (TKey[], TValue[]) Arbre binaire équilibré (Red-Black Tree)
Mémoire Moins de mémoire Plus de mémoire
Insertion Plus lente pour grands ensembles (O(n)) Plus rapide (O(log n))
Accès par index Oui Non
Itération Ordonnée par clé Ordonnée par clé

🧩 Exemple :

SortedList<int, string> list = new SortedList<int, string>();
list.Add(2, "B");
list.Add(1, "A");

SortedDictionary<int, string> dict = new SortedDictionary<int, string>();
dict.Add(2, "B");
dict.Add(1, "A");

🧠 Conclusion :

  • SortedList est meilleur pour petites collections triées.

  • SortedDictionary est meilleur pour insertion/suppression fréquentes.


🔹 7. Quelle est la différence entre IEnumerable et IQueryable ?

Critère IEnumerable IQueryable
Namespace System.Collections.Generic System.Linq
Exécution In-memory (C#) Déférée (souvent base de données)
Filtrage Fait en mémoire Traduit en expression (SQL, etc.)
Source typique Collections (List, Array) Entity Framework, LINQ to SQL
Performance Moins optimisée pour DB Optimisée pour la requête distante

🧩 Exemple :

IEnumerable<User> users = db.Users.ToList().Where(u => u.Age > 30); // Filtrage en mémoire
IQueryable<User> query = db.Users.Where(u => u.Age > 30);           // Filtrage côté SQL

🧠 Conclusion :

IQueryable permet la traduction en requête distante (SQL),
alors que IEnumerable manipule les données déjà chargées.


🔹 8. Comment fonctionne la méthode LINQ Select() et Where() ?

Méthode Rôle Type de retour Exemple
Where() Filtrer les éléments selon une condition IEnumerable<T> list.Where(x => x > 10)
Select() Projeter / transformer chaque élément IEnumerable<TResult> list.Select(x => x * 2)

🧩 Exemple combiné :

List<int> numbers = new() { 1, 2, 3, 4, 5, 6 };

var result = numbers
    .Where(x => x % 2 == 0)  // Filtre (pairs)
    .Select(x => x * x);     // Projection (carré)

foreach (var n in result)
    Console.WriteLine(n); // 4, 16, 36

🧠 Fonctionnement :

Ces méthodes utilisent l’exécution différée :
le traitement n’a lieu que lors de l’itération (foreach, ToList(), etc.).


📘 RÉCAPITULATIF SYNTHÉTIQUE

Concept Description Complexité
List<T> Collection générique, accès indexé O(1)
ArrayList Ancienne collection non typée O(1)
Dictionary<TKey, TValue> Table de hachage clé-valeur O(1)
Queue<T> File FIFO O(1)
Stack<T> Pile LIFO O(1)
LinkedList<T> Liste doublement chaînée O(1) insertion
HashSet<T> Ensemble unique, basé sur hachage O(1)
SortedList vs SortedDictionary Collections triées O(log n)
IEnumerable<T> Exécution en mémoire n/a
IQueryable<T> Exécution distante (SQL) n/a





🔹 1. Quelle est la différence entre SortedSet et HashSet ?

Caractéristique HashSet<T> SortedSet<T>
Ordre Non trié Toujours trié selon Comparer<T>
Recherche O(1) en moyenne O(log n) (arbre rouge-noir)
Complexité insertion O(1) O(log n)
Usage Vérifier présence, collections uniques rapides Collections uniques avec tri automatique

🧩 Exemple :

var hashSet = new HashSet<int> { 3, 1, 2 };
var sortedSet = new SortedSet<int> { 3, 1, 2 };

Console.WriteLine(string.Join(",", hashSet));   // ordre indéfini
Console.WriteLine(string.Join(",", sortedSet)); // 1,2,3

🔹 2. Comment fonctionne un PriorityQueue en C# et quels sont ses avantages ?

✅ Définition :

PriorityQueue<TElement, TPriority> est une file avec priorité : l’élément avec la plus haute priorité (ou la plus basse selon comparaison) est servi en premier.

🧩 Exemple :

var pq = new PriorityQueue<string, int>();
pq.Enqueue("low", 5);
pq.Enqueue("high", 1);

Console.WriteLine(pq.Dequeue()); // "high" (priorité 1)

✅ Avantages :

  • Algorithme heap-based, O(log n) pour insertion et suppression

  • Idéal pour algorithmes de graphe (Dijkstra), gestion de tâches avec priorité


🔹 3. Pourquoi ImmutableDictionary<TKey, TValue> est-il utile ?

✅ Définition :

  • Dictionnaire immuable, toute modification crée une nouvelle instance.

  • Thread-safe par nature.

✅ Avantages :

  • Sécurité multi-thread

  • Prévisibilité (pas de modification inattendue)

  • Compatible avec architecture fonctionnelle / programmation réactive

🧩 Exemple :

var dict = ImmutableDictionary<string, int>.Empty;
var newDict = dict.Add("A", 1);

Console.WriteLine(dict.ContainsKey("A"));    // false
Console.WriteLine(newDict.ContainsKey("A")); // true

🔹 4. Comment optimiser la recherche dans une liste avec BinarySearch ?

✅ Conditions :

  • La liste doit être triée

  • BinarySearch utilise un algorithme diviser-pour-régner, O(log n)

🧩 Exemple :

var list = new List<int> {1, 3, 5, 7, 9};
int index = list.BinarySearch(5); // 2

Si la liste n’est pas triée, trier d’abord avec list.Sort().


🔹 5. Différence entre ConcurrentDictionary<TKey, TValue> et ImmutableDictionary<TKey, TValue>

Caractéristique ConcurrentDictionary ImmutableDictionary
Thread-safety Oui, mutable Oui, immuable
Modification Possible directement Chaque modification → nouvelle instance
Usage Multi-thread performant Multi-thread + sécurité immutabilité, architecture fonctionnelle

🔹 6. Pourquoi et comment utiliser ObservableCollection ?

✅ Objectif :

Collection notifiant les changements (Add, Remove, Reset) à l’UI ou aux observateurs.

🧩 Exemple :

ObservableCollection<string> list = new();
list.CollectionChanged += (s, e) => Console.WriteLine(e.Action);
list.Add("Hello"); // déclenche l’événement CollectionChanged

Utilisé surtout en WPF / MVVM pour liaison dynamique à l’UI.


🔹 7. Meilleure structure pour stocker des données triées fréquemment mises à jour

  • SortedDictionary<TKey, TValue> ou SortedSet : insertion O(log n), tri automatique

  • Pour recherches fréquentes sans modification, ImmutableSortedDictionary pour thread-safety

  • Pour mutations fréquentes et performance multi-thread : combiner ConcurrentDictionary + tri au besoin


🔹 8. Différence entre IDictionary<TKey, TValue> et IReadOnlyDictionary<TKey, TValue>

Caractéristique IDictionary IReadOnlyDictionary
Modification Oui Non
Lecture seule Possible mais mutable Garantie immuable
Usage Collections générales API exposant seulement lecture

🧩 Exemple :

IDictionary<int, string> dict = new Dictionary<int, string>();
IReadOnlyDictionary<int, string> readOnly = dict;

🔹 9. Comment fonctionnent ReadOnlyMemory et ReadOnlySpan ?

Type Stockage Mutable Stack-only Usage
ReadOnlySpan<T> Référence sur mémoire Non Slices rapides, parsing
ReadOnlyMemory<T> Heap ou stack Non Async-friendly, stockage plus long

🧩 Exemple :

var array = new int[] {1,2,3,4};
ReadOnlySpan<int> span = array.AsSpan(0, 2); // [1,2]
ReadOnlyMemory<int> memory = array.AsMemory(0,2); // [1,2]

ReadOnlyMemory<T> peut être stocké ou passé à async contrairement à Span<T>.


🔹 10. Comment fonctionne Tuple<T1, T2, ...> et pourquoi préférer ValueTuple<T1, T2, ...> ?

Tuple<T1,T2,...>

  • Reference type

  • Immutable

  • Syntaxe plus lourde, moins lisible (Item1, Item2)

  • Allocation sur le heap

ValueTuple<T1,T2,...>

  • Value type → pas d’allocation inutile

  • Compatible déconstruction

  • Syntaxe plus claire (var (x,y) = GetValues();)

🧩 Exemple :

// Tuple classique
Tuple<int,string> t1 = Tuple.Create(1,"A");

// ValueTuple
var t2 = (1,"A");
var (id, name) = t2;

✅ Préférer ValueTuple pour performance et lisibilité.


📘 Synthèse des Structures

Collection Usage principal Performance Particularité
HashSet Unicité rapide O(1) Non trié
SortedSet Unicité triée O(log n) Trie automatique
PriorityQueue File avec priorité O(log n) Algorithme de heap
ImmutableDictionary Sécurité thread / immutabilité O(log n) Nouvelle instance à chaque modification
ConcurrentDictionary Multi-thread mutable O(1) Thread-safe
ObservableCollection UI binding O(1) Notifie changements
ReadOnlySpan / ReadOnlyMemory Slices mémoire ⚡ très rapide Span stack-only, Memory heap-async
Tuple / ValueTuple Conteneur multi-valeurs Heap vs stack ValueTuple pour performance & lisibilité


Aucun commentaire:

Enregistrer un commentaire

Q/A Collections Lượt xem: