List w C# — praktyczny przewodnik

List w C# — praktyczny przewodnik

Gdy zaczynasz programować w C#, szybko poznajesz tablice. Są proste i wydajne, ale mają jedno fundamentalne ograniczenie – stały rozmiar. Co jednak, gdy nie wiesz, ile danych będziesz przechowywać? Gdy potrzebujesz swobodnie dodawać i usuwać elementy?

Odpowiedzią jest List<T> – potężna i elastyczna kolekcja, która jest absolutną podstawą w warsztacie każdego programisty .NET.

W .NET bardzo często spotkasz się z kolekcją List<T>, która pełni rolę dynamicznej tablicy. Dzięki temu możesz łatwo przechowywać, dodawać i usuwać elementy, a przy tym korzystać z intuicyjnej składni indeksowania.

👉 W tym wpisie dowiesz się:

  • czym jest List<T> i jak działa „pod maską”,
  • jakie operacje są szybkie, a które mogą być kosztowne,
  • kiedy warto używać List, a kiedy lepszym wyborem będzie Dictionary lub inne kolekcje.

🔹 Czym jest List<T>?

List<T> to, najprościej mówiąc, lista obiektów, której rozmiar może się dynamicznie zmieniać. Można o niej myśleć jak o “inteligentnej tablicy”. List<T> to kolekcja generyczna dostępna w przestrzeni nazw System.Collections.Generic.

Litera T oznacza typ elementów, jakie lista przechowuje (np. int, string, obiekt klasy). T symbolizuje typ generyczny (ogólny). Oznacza to, że tworząc listę, musisz z góry określić, jakiego typu dane będziesz w niej przechowywać. Daje to tzw. bezpieczeństwo typów – kompilator będzie pilnował, abyś do listy liczb nie próbował dodać tekstu.

  • List<string> to lista przechowująca wyłącznie tekst.
  • List<int> to lista przechowująca wyłącznie liczby całkowite.
  • List<User> to lista przechowująca obiekty klasy User.

Jak to działa pod maską?

Wewnętrznie List<T> faktycznie używa zwykłej tablicy do przechowywania danych. Gdy dodajesz element i w tej tablicy zabraknie miejsca, List<T> automatycznie tworzy nową, większą tablicę, przekopiowuje do niej wszystkie stare elementy i dodaje nowy. Dla nas jako programistów ten proces jest niewidoczny, ale warto o nim wiedzieć, analizując wydajność.

👉 Kluczowe cechy:

  • lista przechowuje elementy w wewnętrznej tablicy,
  • jeśli zabraknie miejsca, tworzona jest nowa tablica o większym rozmiarze, a elementy są kopiowane,
  • rozmiar listy rośnie dynamicznie, w przeciwieństwie do zwykłej tablicy (array), która ma stałą długość.

🔹 Tworzenie listy w C#

// Pusta lista liczb całkowitych
var list = new List<int>();

// Tworzymy pustą listę zadań
var tasks = new List<string>();

// Dodajemy elementy na koniec listy za pomocą metody Add()
tasks.Add("Nauczyć się C#");
tasks.Add("Napisać pierwszy program");
tasks.Add("Opanować kolekcje");

// Możemy też od razu zadeklarować listę z początkowymi elementami
var shoppingList = new List<string> { "Chleb", "Masło", "Mleko" };

Jeśli planujesz przechowywać dużą liczbę obiektów na liście, możesz zmniejszyć koszty relokacji wewnętrznej tablicy, ustawiając początkowy rozmiar:

// Tworzenie listy o początkowym rozmiarze
var list = new List<int>(500);

💡 Jeśli z góry wiesz, że lista będzie przechowywać dużo elementów, ustaw początkową pojemność — unikniesz kosztownych operacji kopiowania.

Najczęstsze operacje na liście

var list = new List<int>();

// Dodawanie
list.Add(10);                   // na końcu
list.Insert(5, 120);            // w określonym miejscu (indeks 5)

// Usuwanie
list.Remove(40);                // po wartości
list.RemoveAt(2);               // po indeksie
list.Clear();                   // usuwa wszystkie elementy

// Odczyt i wyszukiwanie
var item = list[3];             // dostęp po indeksie
var index = list.IndexOf(25);   // zwraca indeks elementu
var contains = list.Contains(40); // sprawdza, czy element istnieje
var count = list.Count;         // liczba elementów

// Wyszukiwanie za pomocą predykatu
var citys = new List<City>();
int idx = citys.FindIndex(city => city.TotalPopulation < 300000);

// Iteracja po elementach
foreach (var element in list)
    Console.WriteLine(element);

Zobaczmy teraz, gdzie lista działa dobrze, a gdzie nie.

Dodawanie lub usuwanie elementów na początku lub na środku.
Jeśli dodasz lub usuniesz element na początku lub na środku listy, to Lista musi przesunąć jeden lub więcej elementów w swojej tablicy wewnętrznej. W najgorszym przypadku, jeśli dodasz / usuniesz element na samym początku listy, musi on przesunąć wszystkie istniejące elementy. Im większa lista, tym bardziej kosztowna będzie ta operacja.

Dodawanie lub usuwanie elementów na końcu.
Dodanie lub usunięcie elementu na końcu listy jest stosunkowo szybką operacją i nie zależy od wielkości listy. Istniejące elementy nie muszą być przesuwane. Dlatego koszt tej operacji jest stosunkowo mały i stały i nie zależy od liczby pozycji na liście.

Wyszukiwanie elementu.
Podczas korzystania z metod obejmujących wyszukiwanie elementu (np. IndexOf, Contains and Find), Lista wykonuje wyszukiwanie liniowe. Oznacza to, że iteruje wszystkie elementy w swojej wewnętrznej tablicy i jeśli znajdzie dopasowanie, zwraca je. W najgorszym przypadku, jeśli ten element znajduje się na końcu listy, wszystkie elementy na liście muszą zostać zeskanowane przed znalezieniem dopasowania. Ponownie, jest to kolejny przykład, w którym koszt znalezienia dopasowania jest liniowy i jest wprost proporcjonalny do liczby elementów na liście.

Dostęp do elementu za pomocą indeksu. 
W tym są bardzo dobre listy. Możesz użyć indeksu, aby uzyskać pozycję na liście i bez względu na to, jak duża jest lista, koszt dostępu do pozycji według indeksu pozostaje względnie mały i stały.

Lista w pigułce
Tak więc dodawanie lub usuwanie elementów na końcu listy i uzyskiwanie dostępu do elementów według indeksu to szybkie i wydajne operacje.

Wyszukiwanie pozycji na liście obejmuje wyszukiwanie liniowe. Jeśli chcesz wyszukać elementy na podstawie niektórych kryteriów, a nie indeksu (np. Klient o identyfikatorze 5674), lepiej skorzystaj ze Słownika.

Używaj List<T> gdy:

  • Nie znasz z góry dokładnej liczby elementów.
  • Potrzebujesz swobodnie dodawać i usuwać dane.
  • Głównie odczytujesz dane po indeksie lub iterujesz po całej kolekcji.
  • Wydajność wstawiania/usuwania elementów w środku nie jest krytyczna.

Kiedy rozważyć inną kolekcję?

Jeśli Twoim głównym zadaniem jest błyskawiczne wyszukiwanie elementów na podstawie unikalnego identyfikatora (np. szukanie użytkownika po jego ID lub produktu po kodzie EAN), znacznie lepszym wyborem będzie Dictionary<TKey, TValue>.

Lista czy coś innego?

  • List<T> — najlepsza do kolekcji, gdzie często odwołujesz się po indeksie i dodajesz na końcu.
  • Dictionary<TKey, TValue> — lepszy wybór, gdy potrzebujesz szybkiego wyszukiwania po kluczu (np. Id klienta).
  • LinkedList<T> — warto rozważyć, gdy często dodajesz/usu­wasz elementy w środku.

🔹 Podsumowanie

  • List<T> to dynamiczna tablica w C#, prosta i uniwersalna.
  • Najszybsze operacje: dostęp przez indeks i dodawanie na końcu.
  • Wolniejsze operacje: wyszukiwanie i modyfikacje w środku.
  • Jeśli potrzebujesz częstego wyszukiwania po kluczach → sięgnij po Dictionary.

👉 A Ty częściej używasz List<T> czy Dictionary<TKey, TValue>? Podziel się w komentarzu!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *