mardi 2 septembre 2025

List

📌 Questions / Réponses sur List<T> en C#

1. Quelle est la différence entre List<T> et un tableau (T[]) ?

  • Réponse :

    • Un tableau a une taille fixe, List<T> est dynamique (elle grossit automatiquement si nécessaire).

    • List<T> offre des méthodes pratiques (Add, Remove, Find, etc.) que les tableaux n’ont pas.

    • En mémoire, un tableau est contigu, tandis que List<T> encapsule un tableau interne redimensionné.


2. Quelle est la complexité des opérations courantes sur List<T> ?

  • Réponse :

    • Add : O(1) amorti (mais peut être O(n) si redimensionnement).

    • Insert et Remove : O(n) (car décalage des éléments).

    • Contains / IndexOf : O(n).

    • Accès par index (list[i]) : O(1).


3. Comment fonctionne la capacité (Capacity) et la taille (Count) d’une List<T> ?

  • Réponse :

    • Count = nombre d’éléments réels stockés.

    • Capacity = taille du tableau interne alloué.

    • Quand Count dépasse Capacity, la List alloue un tableau plus grand (souvent le double) et copie les éléments.


4. Quelle est la différence entre List<T> et IEnumerable<T> / ICollection<T> / IList<T> ?

  • Réponse :

    • IEnumerable<T> : permet uniquement l’itération (foreach).

    • ICollection<T> : ajoute des méthodes comme Add, Remove, Count.

    • IList<T> : ajoute l’accès par index et Insert, RemoveAt.

    • List<T> implémente tout ça (héritage des trois).


5. Que se passe-t-il si on ajoute un élément dans une List<T> pendant un foreach ?

  • Réponse :

    • Une exception InvalidOperationException est levée, car la collection est modifiée pendant l’énumération.


6. Quelle est la différence entre Clear() et RemoveAll() ?

  • Réponse :

    • Clear() : supprime tous les éléments, Count devient 0.

    • RemoveAll(predicate) : supprime uniquement les éléments qui satisfont la condition.


7. Comment supprimer efficacement les doublons d’une List<T> ?

  • Réponse :

    • Utiliser LINQ : list = list.Distinct().ToList();

    • Ou utiliser un HashSet<T> pour filtrer.


8. Quelle est la différence entre List<T> et LinkedList<T> ?

  • Réponse :

    • List<T> : basé sur un tableau, accès indexé rapide O(1), insertions/suppressions coûteuses O(n).

    • LinkedList<T> : basé sur des nœuds, insertions/suppressions rapides O(1) si on a la référence du nœud, mais accès par index est O(n).


9. Peut-on rendre une List<T> en lecture seule ?

  • Réponse :

    • Oui, avec list.AsReadOnly().

    • Ou utiliser IReadOnlyList<T>.


10. Quelle est la différence entre List<T>.ForEach() et foreach ?

  • Réponse :

    • ForEach(Action<T>) exécute une action sur chaque élément mais n’autorise pas break/continue.

    • foreach est plus flexible et lisible.




🔥 Questions avancées et pièges sur List<T> en C#

1. Que se passe-t-il si on stocke des struct dans une List<T> ?

  • Réponse :

    • Chaque élément est copié (valeur) lors de l’ajout.

    • Quand on accède à un élément (list[i]), on obtient une copie : modifier ses champs n’affecte pas l’élément dans la liste.

    • Exemple piège :

      struct Point { public int X; public int Y; }
      var list = new List<Point> { new Point { X = 1, Y = 2 } };
      list[0].X = 99; // ne modifie pas l’élément dans la liste !
      

2. Pourquoi List<object> peut entraîner du boxing quand on stocke des valeurs ?

  • Réponse :

    • Si on met un type valeur (int, double, struct…) dans une List<object>, il est boxé (copié dans le heap).

    • Cela dégrade la performance et augmente la mémoire utilisée.

    • Solution : utiliser List<T> avec un T fortement typé.


3. Quelle est la différence entre Capacity et TrimExcess() ?

  • Réponse :

    • Capacity peut être supérieur à Count.

    • TrimExcess() réduit Capacity pour coller au Count réel (libère la mémoire).

    • ⚠️ Attention : TrimExcess() peut être coûteux si appelé fréquemment dans des boucles.


4. Que se passe-t-il si on appelle list.AddRange(list) ?

  • Réponse :

    • On ajoute la liste à elle-même.

    • Résultat : duplication des éléments (aucune exception).

    • Exemple : [1,2,3] devient [1,2,3,1,2,3].


5. Quelle est la différence entre List<T> et ArrayList ?

  • Réponse :

    • ArrayList stocke des object, donc nécessite du boxing/unboxing pour les types valeurs.

    • List<T> est générique et évite ce problème.

    • List<T> est plus performant et typesafe (pas de cast à faire).


6. Quelle est la complexité réelle quand la List<T> double sa capacité ?

  • Réponse :

    • Le Add est O(1) amorti.

    • Mais quand le tableau interne doit s’agrandir : allocation + copie → O(n).

    • Donc, dans le pire cas, Add est O(n).


7. Peut-on modifier une List<T> en parallèle (multithreading) ?

  • Réponse :

    • Non, List<T> n’est pas thread-safe.

    • Plusieurs Add/Remove concurrents → comportement indéfini.

    • Pour la synchronisation : utiliser lock, ou ConcurrentBag<T>, ConcurrentQueue<T>


8. Quelle est la différence entre Find() et Where() (LINQ) ?

  • Réponse :

    • Find() renvoie le premier élément correspondant ou default(T).

    • Where() renvoie un IEnumerable` avec tous les éléments correspondants.

    • FindAll() est l’équivalent proche de Where().ToList().


9. Pourquoi une List<T> peut-elle "fuir" de la mémoire même après Clear() ?

  • Réponse :

    • Clear() met Count = 0, mais les références dans le tableau interne existent toujours (elles sont juste ignorées).

    • Pour vraiment libérer : list = new List<T>() ou TrimExcess() après Clear().


10. Que se passe-t-il si on parcourt une List<T> avec un for et qu’on supprime des éléments ?

  • Réponse :

    • Si on fait list.RemoveAt(i) pendant un for, les indices changent et on risque de sauter des éléments.

    • Solution : parcourir à l’envers (for (int i = list.Count-1; i >= 0; i--)).



🔹 Algorithmes fréquents avec List<T> en C#

1. Parcourir une liste et afficher les éléments

var list = new List<int> { 1, 2, 3, 4, 5 };
foreach (var n in list)
    Console.WriteLine(n);

2. Trouver le minimum et le maximum

var list = new List<int> { 10, 5, 8, 20, 3 };
int min = list.Min();  // 3
int max = list.Max();  // 20

⚠️ En entretien on peut te demander de coder toi-même :

int min = list[0];
foreach (var n in list)
    if (n < min) min = n;

3. Recherche linéaire dans une liste

var list = new List<string> { "Alice", "Bob", "Charlie" };
string target = "Bob";

bool found = list.Contains(target); // True

Sans Contains() (implémentation manuelle) :

bool found = false;
foreach (var name in list)
    if (name == target) { found = true; break; }

4. Supprimer les doublons

var list = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
var noDuplicates = list.Distinct().ToList(); // [1,2,3,4,5]

Implémentation sans LINQ :

var unique = new List<int>();
foreach (var n in list)
    if (!unique.Contains(n))
        unique.Add(n);

5. Inverser une liste

var list = new List<int> { 1, 2, 3, 4, 5 };
list.Reverse(); // [5,4,3,2,1]

Implémentation manuelle (swap) :

for (int i = 0; i < list.Count / 2; i++)
{
    int temp = list[i];
    list[i] = list[list.Count - 1 - i];
    list[list.Count - 1 - i] = temp;
}

6. Tri d’une liste

var list = new List<int> { 5, 3, 8, 1, 4 };
list.Sort(); // [1,3,4,5,8]

Tri décroissant :

list.Sort((a, b) => b.CompareTo(a));

7. Recherche binaire (si la liste est triée)

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

8. Fusion de deux listes

var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 4, 5, 6 };

list1.AddRange(list2); // [1,2,3,4,5,6]

9. Trouver les éléments communs entre deux listes

var list1 = new List<int> { 1, 2, 3, 4 };
var list2 = new List<int> { 3, 4, 5, 6 };

var common = list1.Intersect(list2).ToList(); // [3,4]

10. Rotation d’une liste (décaler les éléments)

Exemple : [1,2,3,4,5][5,1,2,3,4]

var list = new List<int> { 1, 2, 3, 4, 5 };
int last = list[list.Count - 1];
list.RemoveAt(list.Count - 1);
list.Insert(0, last);


Return

Aucun commentaire:

Enregistrer un commentaire

List Lượt xem: