Kolekcje generyczne – List<T> i Dictionary<K,V>

Poznasz potężne struktury danych, które zastępują tablice. Nauczysz się przechowywać, wyszukiwać i przetwarzać dane w elastyczny sposób!

List<T> Dictionary generyki kolekcje System.Collections.Generic
01

Problem – ograniczenia tablic

Tablice w C# mają stały rozmiar. Co jeśli nie wiesz ile elementów będziesz przechowywać?

cs/ProblemTablice.cs C#
// 😰 Tablica ma STAŁY rozmiar
string[] uczniowie = new string[3];
uczniowie[0] = "Jan";
uczniowie[1] = "Anna";
uczniowie[2] = "Piotr";

// Chcemy dodać czwartego ucznia...
uczniowie[3] = "Maria";  // 💥 IndexOutOfRangeException!

// Trzeba tworzyć nową, większą tablicę i kopiować 😞
string[] nowaTab = new string[4];
Array.Copy(uczniowie, nowaTab, uczniowie.Length);
nowaTab[3] = "Maria";
Problemy z tablicami
  • Stały rozmiar – trzeba znać liczbę elementów z góry
  • Brak wbudowanych metod – usuwanie, wstawianie, wyszukiwanie
  • Brak dostępu po kluczu – tylko po indeksie liczbowym

Co jeśli chcemy…

PotrzebaTablicaRozwiązanie
Dodawać elementy dynamicznie❌ Stały rozmiarList<T>
Usuwać z dowolnego miejsca❌ Ręczne przesuwanieList<T>
Szukać po kluczu (np. PESEL → Osoba)❌ Tylko indeksDictionary<K,V>
Sprawdzić czy element istnieje❌ Pętla ręcznaContains()
02

Czym są kolekcje generyczne?

Kolekcje generyczne to elastyczne struktury danych z przestrzeni System.Collections.Generic. Słowo „generyczne” oznacza, że określasz typ elementów przy tworzeniu.

cs/Generyki.cs C#
using System.Collections.Generic;

// Lista liczb całkowitych
List<int> liczby = new List<int>();

// Lista stringów
List<string> imiona = new List<string>();

// Lista własnych obiektów
List<Uczen> uczniowie = new List<Uczen>();

// Słownik: klucz string → wartość int
Dictionary<string, int> oceny = new Dictionary<string, int>();
Analogia: Pudełko z etykietą 📦

Kolekcja generyczna to jak pudełko z etykietą:

  • List<int> = pudełko z etykietą „tylko liczby całkowite”
  • List<string> = pudełko z etykietą „tylko teksty”
  • Kompilator pilnuje, żebyś nie włożył czegoś złego!
Zalety generycznych kolekcji
  • Bezpieczeństwo typów – błędy wykrywane przy kompilacji
  • Wydajność – brak boxing/unboxing
  • Dynamiczny rozmiar – rośnie automatycznie
  • Wbudowane metody – Add, Remove, Find, Sort…

Najważniejsze kolekcje

KolekcjaOpisUżycie
List<T>Dynamiczna listaTablica o zmiennym rozmiarze
Dictionary<K,V>Słownik klucz-wartośćSzybkie wyszukiwanie po kluczu
HashSet<T>Zbiór unikalnychBez duplikatów
Queue<T>Kolejka FIFOPierwszy wchodzi, pierwszy wychodzi
Stack<T>Stos LIFOOstatni wchodzi, pierwszy wychodzi
03

List<T> – dynamiczna lista

List<T> to dynamiczna tablica – rośnie automatycznie gdy dodajesz elementy. To najczęściej używana kolekcja!

cs/ListPodstawy.cs C#
using System.Collections.Generic;

// Tworzenie pustej listy
List<string> imiona = new List<string>();

// Dodawanie elementów
imiona.Add("Jan");
imiona.Add("Anna");
imiona.Add("Piotr");

// Dostęp przez indeks (jak tablica)
Console.WriteLine(imiona[0]);  // Jan
Console.WriteLine(imiona[1]);  // Anna

// Liczba elementów
Console.WriteLine(imiona.Count);  // 3

// Iteracja
foreach (string imie in imiona)
{
    Console.WriteLine(imie);
}

Inicjalizacja z wartościami

cs/ListInicjalizacja.cs C#
// Inicjalizacja z wartościami (collection initializer)
List<int> liczby = new List<int> { 1, 2, 3, 4, 5 };

// Skrócona składnia (C# 9+)
List<string> owoce = new() { "Jabłko", "Gruszka", "Banan" };

// Z tablicy
int[] tablica = { 10, 20, 30 };
List<int> lista = new List<int>(tablica);

// Lista obiektów
List<Uczen> uczniowie = new List<Uczen>
{
    new Uczen { Imie = "Jan", Wiek = 17 },
    new Uczen { Imie = "Anna", Wiek = 16 }
};
List<string>
Count: 3
[0]
„Jan”
[1]
„Anna”
[2]
„Piotr”
[3]
Add()→
04

List<T> – metody

MetodaOpisPrzykład
Add(item)Dodaje na końculista.Add("X")
AddRange(collection)Dodaje wiele elementówlista.AddRange(tablica)
Insert(index, item)Wstawia na pozycjilista.Insert(0, "X")
Remove(item)Usuwa pierwszy pasującylista.Remove("X")
RemoveAt(index)Usuwa na pozycjilista.RemoveAt(0)
Clear()Usuwa wszystkolista.Clear()
Contains(item)Czy zawiera?lista.Contains("X")
IndexOf(item)Pozycja elementulista.IndexOf("X")
CountLiczba elementówlista.Count
ToArray()Konwersja na tablicęlista.ToArray()
cs/ListMetody.cs C#
List<string> owoce = new List<string> { "Jabłko", "Gruszka" };

// Dodawanie
owoce.Add("Banan");                    // Na końcu
owoce.Insert(0, "Pomarańcza");          // Na początku
owoce.AddRange(new[] { "Kiwi", "Mango" });  // Wiele naraz

// Stan: Pomarańcza, Jabłko, Gruszka, Banan, Kiwi, Mango

// Usuwanie
owoce.Remove("Gruszka");   // Usuwa pierwszy "Gruszka"
owoce.RemoveAt(0);         // Usuwa pierwszy element

// Sprawdzanie
bool maJablko = owoce.Contains("Jabłko");  // true
int pozycja = owoce.IndexOf("Banan");      // 1 (lub -1 jeśli brak)
int ile = owoce.Count;                     // 4

// Konwersja na tablicę
string[] tablica = owoce.ToArray();

// Czyszczenie
owoce.Clear();  // Pusta lista, Count = 0
05

List<T> – wyszukiwanie

cs/ListWyszukiwanie.cs C#
List<int> liczby = new List<int> { 5, 12, 3, 18, 7, 22, 9 };

// Find – znajdź PIERWSZY pasujący
int pierwszyWiekszy = liczby.Find(n => n > 10);  // 12

// FindAll – znajdź WSZYSTKIE pasujące
List<int> wszystkieWieksze = liczby.FindAll(n => n > 10);  // [12, 18, 22]

// FindIndex – pozycja pierwszego pasującego
int indeks = liczby.FindIndex(n => n > 10);  // 1

// FindLast – ostatni pasujący
int ostatni = liczby.FindLast(n => n > 10);  // 22

// Exists – czy istnieje pasujący?
bool czyJest = liczby.Exists(n => n > 100);  // false

// TrueForAll – czy wszystkie spełniają warunek?
bool wszystkieDodatnie = liczby.TrueForAll(n => n > 0);  // true

Wyszukiwanie obiektów

cs/FindObiektow.cs C#
public class Produkt
{
    public string Nazwa { get; set; }
    public decimal Cena { get; set; }
}

List<Produkt> produkty = new List<Produkt>
{
    new Produkt { Nazwa = "Laptop", Cena = 3500 },
    new Produkt { Nazwa = "Myszka", Cena = 80 },
    new Produkt { Nazwa = "Monitor", Cena = 1200 }
};

// Znajdź produkt po nazwie
Produkt laptop = produkty.Find(p => p.Nazwa == "Laptop");

// Znajdź wszystkie drogie (powyżej 1000 zł)
List<Produkt> drogie = produkty.FindAll(p => p.Cena > 1000);

// Znajdź najtańszy (pętlą)
Produkt najtanszy = produkty[0];
foreach (var p in produkty)
{
    if (p.Cena < najtanszy.Cena)
        najtanszy = p;
}
06

List<T> – sortowanie

cs/ListSortowanie.cs C#
List<int> liczby = new List<int> { 5, 2, 8, 1, 9 };

// Sortowanie rosnąco
liczby.Sort();  // [1, 2, 5, 8, 9]

// Sortowanie malejąco
liczby.Reverse();  // [9, 8, 5, 2, 1]

// Sortowanie stringów
List<string> imiona = new List<string> { "Zofia", "Anna", "Jan" };
imiona.Sort();  // Anna, Jan, Zofia (alfabetycznie)

Sortowanie obiektów

cs/SortowanieObiektow.cs C#
List<Produkt> produkty = new List<Produkt>
{
    new Produkt { Nazwa = "Laptop", Cena = 3500 },
    new Produkt { Nazwa = "Myszka", Cena = 80 },
    new Produkt { Nazwa = "Monitor", Cena = 1200 }
};

// Sortuj po cenie rosnąco
produkty.Sort((a, b) => a.Cena.CompareTo(b.Cena));
// Myszka (80), Monitor (1200), Laptop (3500)

// Sortuj po cenie malejąco
produkty.Sort((a, b) => b.Cena.CompareTo(a.Cena));

// Sortuj po nazwie
produkty.Sort((a, b) => a.Nazwa.CompareTo(b.Nazwa));
07

Dictionary<K,V> – słownik

Dictionary<K,V> to kolekcja par klucz-wartość. Pozwala błyskawicznie znaleźć wartość po kluczu. Działa jak prawdziwy słownik: słowo → definicja.

Analogia: Książka telefoniczna 📞
  • Klucz = nazwisko osoby (unikalne!)
  • Wartość = numer telefonu
  • Szukasz po nazwisku → dostajesz numer natychmiast!
cs/DictionaryPodstawy.cs C#
using System.Collections.Generic;

// Tworzenie słownika: klucz string, wartość int
Dictionary<string, int> wiek = new Dictionary<string, int>();

// Dodawanie par klucz-wartość
wiek["Jan"] = 25;
wiek["Anna"] = 30;
wiek["Piotr"] = 22;

// Odczyt po kluczu
Console.WriteLine(wiek["Anna"]);  // 30

// Liczba elementów
Console.WriteLine(wiek.Count);  // 3

Inicjalizacja z wartościami

cs/DictionaryInit.cs C#
// Inicjalizacja z wartościami
Dictionary<string, string> stolice = new Dictionary<string, string>
{
    { "Polska", "Warszawa" },
    { "Niemcy", "Berlin" },
    { "Francja", "Paryż" }
};

// Alternatywna składnia (C# 6+)
Dictionary<string, int> oceny = new Dictionary<string, int>
{
    ["Jan"] = 5,
    ["Anna"] = 4,
    ["Piotr"] = 3
};

Console.WriteLine(stolice["Polska"]);  // Warszawa
Console.WriteLine(oceny["Anna"]);      // 4
Dictionary<string, int>
wiek
„Jan”
→ 25
„Anna”
→ 30
„Piotr”
→ 22
08

Dictionary – operacje

OperacjaMetodaPrzykład
Dodaj/Ustawdict[key] = valuewiek["Jan"] = 25
Dodaj (jeśli brak)Add(key, value)wiek.Add("Jan", 25)
Pobierzdict[key]wiek["Jan"]
UsuńRemove(key)wiek.Remove("Jan")
WyczyśćClear()wiek.Clear()
Czy ma klucz?ContainsKey(key)wiek.ContainsKey("Jan")
Czy ma wartość?ContainsValue(val)wiek.ContainsValue(25)
LiczbaCountwiek.Count
KluczeKeyswiek.Keys
WartościValueswiek.Values
cs/DictionaryOperacje.cs C#
Dictionary<string, decimal> ceny = new Dictionary<string, decimal>();

// Dodawanie
ceny["Laptop"] = 2500;           // Dodaje lub nadpisuje
ceny.Add("Myszka", 50);           // Tylko dodaje (błąd jeśli istnieje!)

// Modyfikacja
ceny["Laptop"] = 2300;           // Zmiana wartości

// Usuwanie
ceny.Remove("Myszka");            // Usuwa parę

// Sprawdzanie
bool maLaptop = ceny.ContainsKey("Laptop");    // true
bool maCene = ceny.ContainsValue(2300);       // true

// Wszystkie klucze i wartości
foreach (string produkt in ceny.Keys)
    Console.WriteLine(produkt);

foreach (decimal cena in ceny.Values)
    Console.WriteLine(cena);
Add vs operator []
  • dict.Add(key, value) – rzuca wyjątek jeśli klucz istnieje!
  • dict[key] = value – dodaje lub nadpisuje (bezpieczniej)
09

Dictionary – bezpieczny dostęp

Odwołanie do nieistniejącego klucza rzuca KeyNotFoundException! Są bezpieczniejsze metody.

cs/DictionaryBezpieczny.cs C#
Dictionary<string, int> oceny = new Dictionary<string, int>
{
    ["Jan"] = 5,
    ["Anna"] = 4
};

// ❌ NIEBEZPIECZNE – rzuca wyjątek!
// int ocena = oceny["Piotr"];  // KeyNotFoundException!

// ✅ Sposób 1: ContainsKey
if (oceny.ContainsKey("Piotr"))
{
    Console.WriteLine(oceny["Piotr"]);
}
else
{
    Console.WriteLine("Brak oceny");
}

// ✅ Sposób 2: TryGetValue (NAJLEPSZY!)
if (oceny.TryGetValue("Piotr", out int ocena))
{
    Console.WriteLine($"Ocena: {ocena}");
}
else
{
    Console.WriteLine("Brak oceny");
}

// ✅ Sposób 3: GetValueOrDefault (C# 7.1+)
int wynik = oceny.GetValueOrDefault("Piotr", 0);  // 0 jeśli brak
TryGetValue – najlepsza praktyka

TryGetValue sprawdza i pobiera w jednej operacji – szybsze niż ContainsKey + odczyt!

10

Dictionary – zliczanie wystąpień

Klasyczne zastosowanie słownika: zliczanie ile razy coś wystąpiło. Klucz = element, Wartość = liczba wystąpień.

Przypadki użycia
  • Ile razy każda litera występuje w tekście?
  • Ile razy każde słowo występuje w dokumencie?
  • Ile zamówień złożył każdy klient?
  • Ile głosów dostał każdy kandydat?

Przykład 1: Zliczanie liter

cs/ZliczanieLiter.cs C#
string tekst = "programowanie";

// Słownik: litera → liczba wystąpień
Dictionary<char, int> licznik = new Dictionary<char, int>();

foreach (char litera in tekst)
{
    if (licznik.ContainsKey(litera))
    {
        // Litera już była – zwiększ licznik
        licznik[litera]++;
    }
    else
    {
        // Pierwsza litera – ustaw na 1
        licznik[litera] = 1;
    }
}

// Wyświetl wyniki
foreach (var para in licznik)
{
    Console.WriteLine($"'{para.Key}' → {para.Value}x");
}

// Wynik:
// 'p' → 1x
// 'r' → 2x
// 'o' → 2x
// 'g' → 1x
// 'a' → 2x
// 'm' → 1x
// 'w' → 1x
// 'n' → 1x
// 'i' → 1x
// 'e' → 1x

Skrócona wersja z TryGetValue

cs/ZliczanieSkrocone.cs C#
string tekst = "abrakadabra";
Dictionary<char, int> licznik = new Dictionary<char, int>();

foreach (char c in tekst)
{
    // TryGetValue zwraca 0 jeśli klucz nie istnieje
    licznik.TryGetValue(c, out int count);
    licznik[c] = count + 1;
}

// Jeszcze krócej (C# 8+)
foreach (char c in tekst)
{
    licznik[c] = licznik.GetValueOrDefault(c, 0) + 1;
}

// Wynik dla "abrakadabra":
// 'a' → 5x
// 'b' → 2x
// 'r' → 2x
// 'k' → 1x
// 'd' → 1x

Przykład 2: Zliczanie słów

cs/ZliczanieSlow.cs C#
string tekst = "to jest tekst który jest przykładem tego jak to działa";
string[] slowa = tekst.Split(' ');

Dictionary<string, int> licznikSlow = new Dictionary<string, int>();

foreach (string slowo in slowa)
{
    string male = slowo.ToLower();  // Ignoruj wielkość liter
    licznikSlow[male] = licznikSlow.GetValueOrDefault(male, 0) + 1;
}

// Znajdź najczęstsze słowo
string najczestsze = "";
int maxCount = 0;

foreach (var para in licznikSlow)
{
    if (para.Value > maxCount)
    {
        maxCount = para.Value;
        najczestsze = para.Key;
    }
}

Console.WriteLine($"Najczęstsze: '{najczestsze}' ({maxCount}x)");
// Najczęstsze: 'jest' (2x) lub 'to' (2x)

Przykład 3: Głosowanie

cs/Glosowanie.cs C#
string[] glosy = { "A", "B", "A", "C", "A", "B", "A", "C", "C", "C" };

Dictionary<string, int> wyniki = new Dictionary<string, int>();

foreach (string kandydat in glosy)
{
    wyniki[kandydat] = wyniki.GetValueOrDefault(kandydat, 0) + 1;
}

// Wyświetl wyniki
Console.WriteLine("=== WYNIKI GŁOSOWANIA ===");
foreach (var w in wyniki)
{
    Console.WriteLine($"Kandydat {w.Key}: {w.Value} głosów");
}

// Znajdź zwycięzcę
string zwyciezca = "";
int maxGlosow = 0;

foreach (var w in wyniki)
{
    if (w.Value > maxGlosow)
    {
        maxGlosow = w.Value;
        zwyciezca = w.Key;
    }
}

Console.WriteLine($"Zwycięzca: {zwyciezca} ({maxGlosow} głosów)");
Wzorzec zliczania
// Uniwersalny wzorzec:
foreach (var element in kolekcja)
{
    slownik[element] = slownik.GetValueOrDefault(element, 0) + 1;
}
11

Dictionary – iteracja

cs/DictionaryIteracja.cs C#
Dictionary<string, int> oceny = new Dictionary<string, int>
{
    ["Jan"] = 5,
    ["Anna"] = 4,
    ["Piotr"] = 3
};

// Sposób 1: foreach z KeyValuePair
foreach (KeyValuePair<string, int> para in oceny)
{
    Console.WriteLine($"{para.Key}: {para.Value}");
}

// Sposób 2: foreach z var (krócej)
foreach (var para in oceny)
{
    Console.WriteLine($"{para.Key}: {para.Value}");
}

// Sposób 3: dekonstrukcja (C# 7+)
foreach ((string imie, int ocena) in oceny)
{
    Console.WriteLine($"{imie}: {ocena}");
}

// Tylko klucze
foreach (string imie in oceny.Keys)
{
    Console.WriteLine(imie);
}

// Tylko wartości
foreach (int ocena in oceny.Values)
{
    Console.WriteLine(ocena);
}
12

Inne kolekcje

HashSet<T> – zbiór unikalnych

cs/HashSet.cs C#
// HashSet – automatycznie usuwa duplikaty
HashSet<int> unikalne = new HashSet<int>();

unikalne.Add(1);
unikalne.Add(2);
unikalne.Add(1);  // Ignorowane – już jest!
unikalne.Add(3);

Console.WriteLine(unikalne.Count);  // 3 (nie 4!)

// Szybkie sprawdzanie
bool ma = unikalne.Contains(2);  // true – O(1)!

Queue<T> – kolejka FIFO

cs/Queue.cs C#
// Kolejka – pierwszy wchodzi, pierwszy wychodzi
Queue<string> kolejka = new Queue<string>();

kolejka.Enqueue("Jan");    // Dodaj na koniec
kolejka.Enqueue("Anna");
kolejka.Enqueue("Piotr");

string pierwszy = kolejka.Dequeue();  // "Jan" – zdejmij z początku
string nastepny = kolejka.Peek();     // "Anna" – podejrzyj (bez usuwania)

Stack<T> – stos LIFO

cs/Stack.cs C#
// Stos – ostatni wchodzi, pierwszy wychodzi
Stack<string> stos = new Stack<string>();

stos.Push("Pierwsza");   // Połóż na górę
stos.Push("Druga");
stos.Push("Trzecia");

string gora = stos.Pop();   // "Trzecia" – zdejmij z góry
string szczyt = stos.Peek();  // "Druga" – podejrzyj
13

Kiedy której używać?

PotrzebaKolekcjaPrzykład
Lista elementów o zmiennej długościList<T>Lista zakupów, uczniów
Szybkie szukanie po kluczuDictionary<K,V>PESEL → Osoba, ID → Produkt
Zliczanie wystąpieńDictionary<T,int>Litera → ile razy
Bez duplikatówHashSet<T>Unikalne ID, tagi
Kolejność FIFOQueue<T>Kolejka do kasy, zadania
Kolejność LIFOStack<T>Cofnij/Ponów, historia
Złożoność czasowa
List<T> Contains/IndexOfO(n) – wolne dla dużych list
Dictionary / HashSet ContainsO(1) – błyskawiczne!

Jeśli często sprawdzasz „czy zawiera?” – użyj Dictionary lub HashSet!

14

Częste błędy

❌ Błąd 1: Odwołanie do nieistniejącego klucza

❌ Źle – wyjątek!

var dict = new Dictionary<string, int>();
int x = dict["brak"];  // KeyNotFoundException!

✅ Dobrze

if (dict.TryGetValue("brak", out int x))
{
    // użyj x
}

❌ Błąd 2: Add z duplikatem klucza

❌ Źle – wyjątek!

dict.Add("Jan", 5);
dict.Add("Jan", 6);  // ArgumentException!

✅ Dobrze

dict["Jan"] = 5;
dict["Jan"] = 6;  // Nadpisuje – OK

❌ Błąd 3: Modyfikacja podczas iteracji

❌ Źle – wyjątek!

foreach (var item in lista)
{
    if (item == "X")
        lista.Remove(item);  // CRASH!
}

✅ Dobrze

lista.RemoveAll(item => item == "X");

// lub: iteruj po kopii
foreach (var item in lista.ToList())
{
    if (item == "X")
        lista.Remove(item);
}

❌ Błąd 4: Zapomnienie using

❌ Źle – błąd kompilacji

// Brak using!
List<int> lista = new List<int>();
// CS0246: The type 'List<>' could not be found

✅ Dobrze

using System.Collections.Generic;

List<int> lista = new List<int>();
15

Podsumowanie

Kluczowe pojęcia
  • List<T> – dynamiczna lista, zastępuje tablice
  • Dictionary<K,V> – słownik klucz-wartość, szybkie wyszukiwanie
  • HashSet<T> – zbiór unikalnych elementów
  • Queue<T> – kolejka FIFO
  • Stack<T> – stos LIFO

Schemat użycia

cs/Schemat.cs C#
using System.Collections.Generic;

// Lista
List<string> lista = new List<string> { "A", "B" };
lista.Add("C");
lista.Remove("A");
bool ma = lista.Contains("B");

// Słownik
Dictionary<string, int> dict = new Dictionary<string, int>();
dict["klucz"] = 123;
if (dict.TryGetValue("klucz", out int val)) { }

// Zliczanie
foreach (var el in kolekcja)
    dict[el] = dict.GetValueOrDefault(el, 0) + 1;
Zadania

Zadania praktyczne

📝 Zadanie 1: Lista zakupów

Napisz program z menu:

  • 1 – Dodaj produkt
  • 2 – Usuń produkt
  • 3 – Wyświetl listę
  • 4 – Sprawdź czy jest produkt
  • 0 – Wyjście

📝 Zadanie 2: Książka telefoniczna

Utwórz Dictionary<string, string> (imię → telefon).

  • Dodawanie kontaktu
  • Wyszukiwanie numeru po imieniu
  • Usuwanie kontaktu
  • Wyświetlanie wszystkich

📝 Zadanie 3: Zliczanie liter

Pobierz tekst od użytkownika i wyświetl ile razy występuje każda litera (bez spacji, bez rozróżniania wielkości).

💡 Posortuj wyniki malejąco po liczbie wystąpień

⭐ Zadanie 4: Oceny uczniów

Utwórz Dictionary<string, List<int>> gdzie klucz to imię ucznia, a wartość to lista jego ocen.

  • Dodawanie oceny dla ucznia
  • Obliczanie średniej ucznia
  • Wyświetlanie rankingu (po średniej)

⭐⭐ Zadanie 5: Anagram

Napisz metodę bool CzyAnagram(string a, string b) która sprawdza czy dwa słowa są anagramami (te same litery, inna kolejność).

💡 Użyj Dictionary do zliczenia liter w obu słowach i porównaj