KezdőOlvasási idő: ~16 perc

Alap interfészek

List, Set, Map, Queue és Deque interfészek

A Java Collections Framework interfész-hierarchiája — List, Set, Map, Queue, Deque — mindegyik egy jól meghatározott szerződést definiál az adatok tárolására és elérésére.


1. Definíció

Mi ez?

A Java Collections Framework (JCF) egy egységes architektúra objektumcsoportok reprezentálására és manipulálására. A középpontjában interfészek állnak, amelyek a szerződéseket definiálják — a mit, nem a hogyan — a különböző gyűjteménytípusokhoz.

Miért létezik?

A JCF előtt (Java 1.2 előtt) csak Vector, Stack, Hashtable és tömbök léteztek — következetlen API-kkal. A JCF egy koherens hierarchiát hozott létre, hogy:

  • Az algoritmusok interfészek ellen legyenek írva, nem konkrét osztályok ellen
  • Az implementációk felcserélhetők legyenek a hívók módosítása nélkül
  • A közös műveletek (iteráció, rendezés, szűrés) egységesen működjenek

Hol helyezkedik el?

A fő interfész-hierarchia röviden:

  • Az Iterable áll a legfelső szinten.
  • A Collection az Iterable-ből ágazik tovább.
  • List — rendezett ág, duplikátumokat megenged.
  • Set — duplikátummentes ág, ezen belül SortedSet és NavigableSet változatokkal.
  • Queue — FIFO vagy prioritásos elérés, ezen belül a kétvégű Deque.
  • A Map külön kulcs-érték hierarchia, amelynek rendezett változatai a SortedMap és NavigableMap.

2. Alapfogalmak

2.1 `Collection` — az gyökér interfész

A gyűjtemény-hierarchia gyökere (a Map kivételével). Biztosítja:

  • add(E e), remove(Object o), contains(Object o)
  • size(), isEmpty(), clear()
  • iterator(), forEach(), stream()
  • Tömeges műveletek: addAll, removeAll, retainAll, containsAll

2.1.1 Kulcsszavak és fogalmak, amelyeket itt pontosan érteni kell

Ebben a témában nem elég az implementációk neveit megjegyezni. A következő kulcsszavak és fogalmak pontos szerepe az alap:

  • interface — szerződést definiál, nem konkrét tárolási stratégiát.
  • extends — interfész is bővíthet másik interfészt, például a Collection az Iterable-t.
  • implements — a konkrét osztály teljesíti az interfész szerződését, például az ArrayList a List-et.
  • Iterable — azt garantálja, hogy for-each formában bejárható a struktúra.
  • Collection — közös alap a legtöbb elem-alapú gyűjteményhez, de a Map nem része ennek.
  • List — sorrend és pozíció is része a szerződésnek.
  • Set — az egyediség a szerződés része.
  • Map — kulcs-érték asszociáció, külön hierarchia.
  • Queue / Deque — hozzáférési szemantikát fejeznek ki, nem csak adattárolást.

Ezért interjún és API-tervezésnél mindig mondd ki, hogy melyik szó milyen szerződést ígér, és ne csak azt, hogy „ez egy kollekció”.

2.2 `List` — rendezett, index-alapú elérés

Tulajdonság Részletek
Sorrend Beszúrási sorrend megőrzött
Duplikátumok ✅ Megengedett
Index elérés get(int index)
Null elemek ✅ Általában megengedett
Fő implementációk ArrayList, LinkedList, CopyOnWriteArrayList

Plusz metódusok: get(int), set(int, E), add(int, E), remove(int), indexOf(Object), subList(from, to), sort(Comparator).

2.3 `Set` — nincs duplikátum

Tulajdonság Részletek
Sorrend Implementációfüggő (nincs / beszúrási / rendezett)
Duplikátumok ❌ Nem megengedett (equals/hashCode alapján)
Index elérés ❌ Nincs get(int)
Null elemek ✅ Legfeljebb egy null
Fő implementációk HashSet, LinkedHashSet, TreeSet

SortedSet plusz: first(), last(), headSet(to), tailSet(from), subSet(from, to). NavigableSet plusz: floor(e), ceiling(e), lower(e), higher(e), descendingSet().

2.4 `Queue` — FIFO elérés

Művelet Kivételt dob Speciális értéket ad
Beszúrás add(e) offer(e)false
Fejelem törlés remove() poll()null
Fejelem megnézés element() peek()null

Fő implementációk: LinkedList, ArrayDeque, PriorityQueue.

2.5 `Deque` — kétoldalú sor (Queue kiterjesztése)

Mindkét végén enged beszúrást és törlést. Stack (LIFO) gyanánt is használható.

Művelet Eleje Vége
Beszúrás addFirst(e) / offerFirst(e) addLast(e) / offerLast(e)
Törlés removeFirst() / pollFirst() removeLast() / pollLast()
Megnézés peekFirst() peekLast()
Stack használat push(e) = addFirst pop() = removeFirst

Fő implementációk: ArrayDeque (ajánlott), LinkedList.

2.6 `Map` — kulcs-érték párok

A Map nem terjeszti ki a Collection-t. Legfontosabb műveletek:

  • put(K, V), get(K), remove(K), containsKey(K), containsValue(V)
  • keySet()Set<K>, values()Collection<V>, entrySet()Set<Map.Entry<K,V>>
  • Java 8+: getOrDefault, putIfAbsent, computeIfAbsent, merge, forEach

SortedMap plusz: firstKey(), lastKey(), headMap(to), tailMap(from), subMap(from, to).

2.7 Iterálási sorrend és bejárási garanciák

Az interfészválasztás azt is meghatározza, hogy mit feltételezhetsz biztonsággal az iterálásról.

  • a List stabil pozicionális sorrendet ad
  • a HashSet és HashMap nem garantál iterálási sorrendet
  • a LinkedHashSet és LinkedHashMap megőrzi a beszúrási sorrendet
  • a TreeSet és TreeMap rendezett sorrendben iterál
  • a PriorityQueue a poll() során prioritási sorrendet ad, de az általános iterálás nem ezt tükrözi

Ez különösen fontos teszteknél, logolásnál, szerializációnál és felhasználói kimenetnél.

2.8 Csak olvasható nézet vs immutábilis kollekció

A kollekciók a módosíthatóság szempontjából is eltérhetnek:

  • a Collections.unmodifiableList(lista) csak olvasható nézetet ad
  • a List.of(...) valóban immutábilis kollekciót ad
  • a List.copyOf(...) immutábilis pillanatképet készít

Ez nem csak szintaktikai különbség, hanem API-tervezési kérdés is.


3. Gyakorlati használat

Mikor melyik interfészt?

Forgatókönyv Interfész Miért
Rendezett elemek, index-elérés szükséges List O(1) pozicionális elérés
Egyedi elemek, gyors tagság-ellenőrzés Set contains O(1)-ben (HashSet)
Kulcs → érték keresés Map O(1) keresés kulcs alapján
Feladatsor / eseményfeldolgozás Queue FIFO szemantika
Undo/redo stack, böngésző history Deque Mindkét vége elérhető
Rendezett elemek, tartomány-lekérdezések SortedSet / SortedMap Természetes vagy egyedi sorrend

Interfészek ellen programozz, ne implementáció ellen

// ✅ Interfész ellen programozz
List<String> nevek = new ArrayList<>();
Map<String, Integer> pontszamok = new HashMap<>();

// ❌ Túl specifikus implementáció
ArrayList<String> nevek = new ArrayList<>();  // elveszíti a rugalmasságot

Mikor NE használj Collection-t?

  • Fix méretű numerikus adatok → primitív tömbök
  • Magas konkurenciájú frissítések → java.util.concurrent típusok
  • Stream pipeline közbülső eredmények → ne gyűjtsd be korán

Gyors API-tervezési ellenőrzőlista

Amikor kollekciót teszel ki egy API-ban, kérdezd meg:

  1. Része-e a szerződésnek a sorrend?
  2. Jelentése van-e a duplikátumoknak?
  3. Kell-e index szerinti elérés?
  4. Kell-e tagságellenőrzés?
  5. Módosíthatja-e a hívó az eredményt?

Ezek a válaszok általában megmutatják a legszűkebb helyes interfészt.


4. Kód példák

Alappélda — Interfész-szerződések működés közben

import java.util.*;

public class InterfaceSzerzodések {
    public static void main(String[] args) {
        // List — sorrend megőrzött, duplikátumok engedélyezve
        List<String> lista = new ArrayList<>(List.of("banán", "alma", "alma", "cseresznye"));
        System.out.println(lista);              // [banán, alma, alma, cseresznye]
        System.out.println(lista.get(1));       // alma

        // Set — nincs duplikátum, iterálási sorrend nem garantált (HashSet)
        Set<String> halmaz = new HashSet<>(lista);
        System.out.println(halmaz.size());      // 3 (alma deduplication)

        // Queue — FIFO
        Queue<String> sor = new LinkedList<>(List.of("első", "második", "harmadik"));
        System.out.println(sor.poll());         // első
        System.out.println(sor.peek());         // második

        // Deque mint stack — LIFO
        Deque<String> verem = new ArrayDeque<>();
        verem.push("a"); verem.push("b"); verem.push("c");
        System.out.println(verem.pop());        // c (LIFO)

        // Map — kulcs-érték
        Map<String, Integer> map = new HashMap<>();
        map.put("Alice", 90); map.put("Bob", 85);
        System.out.println(map.get("Alice"));  // 90
        System.out.println(map.getOrDefault("Charlie", 0)); // 0
    }
}

Haladó példa — Generikus algoritmus interfészek ellen

public class GyujtemenySegéd {
    // Bármely List implementációval működik
    public static <T extends Comparable<T>> T maximumot(List<T> lista) {
        if (lista.isEmpty()) throw new NoSuchElementException();
        T max = lista.get(0);
        for (T elem : lista) {
            if (elem.compareTo(max) > 0) max = elem;
        }
        return max;
    }

    // Bármely Collection-nel működik
    public static <T> void mindenKiírása(Collection<T> gyujtemeny) {
        gyujtemeny.forEach(System.out::println);
    }

    // Nem módosítható nézetet ad vissza — csak List szerződést kap a hívó
    public static List<String> getNames() {
        List<String> nevek = new ArrayList<>(List.of("Alice", "Bob", "Charlie"));
        return Collections.unmodifiableList(nevek);
    }
}

Gyakori hiba — Módosítás iterálás közben

List<String> elemek = new ArrayList<>(List.of("a", "b", "c", "b"));

// ❌ ConcurrentModificationException!
for (String s : elemek) {
    if (s.equals("b")) elemek.remove(s);
}

// ✅ Iterator remove() metódusa
Iterator<String> it = elemek.iterator();
while (it.hasNext()) {
    if (it.next().equals("b")) it.remove();
}

// ✅ Vagy removeIf (Java 8+)
elemek.removeIf(s -> s.equals("b"));

5. Trade-offok

Interfész ⚡ Elérési sebesség 💾 Memória 🔧 Funkciók Felhasználási eset
List O(1) index (tömb), O(n) keresés Közepes Index, rendezés, subList Rendezett sorozatok
Set O(1) contains (hash), O(log n) (fa) Alacsony–Közepes Egyediség Tagság ellenőrzés, deduplication
Queue O(1) fejelem műveletek Alacsony FIFO szemantika Feladat ütemezés
Deque O(1) mindkét vég Alacsony Stack + Queue Undo, sliding window
Map O(1) kulcs alapján (hash), O(log n) (fa) Közepes–Magas Kulcs-érték, merge Cache, indexelés

6. Gyakori hibák

1. `List` használata, ha egyediség szükséges

// ❌ Duplikátumokat enged — hiba, ha egyedi elemek kellenek
List<String> cimkek = new ArrayList<>();
cimkek.add("java"); cimkek.add("java"); // csendes duplikátum

// ✅ Set használata
Set<String> cimkek = new HashSet<>();
cimkek.add("java"); cimkek.add("java"); // méret marad 1

2. Régi `Stack` osztály használata

// ❌ A Stack kiterjeszti a Vector-t — szinkronizált, legacy, kerülendő!
Stack<Integer> verem = new Stack<>();

// ✅ Deque használata
Deque<Integer> verem = new ArrayDeque<>();
verem.push(1); verem.pop();

3. Null kezelési különbségek figyelmen kívül hagyása

// TreeSet/TreeMap: null NullPointerException-t dob
TreeSet<String> rendezett = new TreeSet<>();
rendezett.add(null); // ❌ NullPointerException — null-t nem lehet összehasonlítani

// HashSet: egy null megengedett
HashSet<String> hashelve = new HashSet<>();
hashelve.add(null); // ✅ OK

4. `remove(Object)` vs `remove(int)` keveredés List-en

List<Integer> szamok = new ArrayList<>(List.of(1, 2, 3, 4));
szamok.remove(1);                    // INDEX alapján töröl → [1, 3, 4]
szamok.remove(Integer.valueOf(3));   // ÉRTÉK alapján töröl → [1, 4]

7. Mélymerülés

Interfész szegregáció API tervezésben

Mindig a legszűkebb hasznos típust add vissza a metódusodból. Ha a hívóknak csak iterálni kell, visszaadhatsz Iterable<T>-t. Ha tagság-ellenőrzés kell, Collection<T> elegendő. Csak akkor adj vissza List-et, ha az index-elérés valóban szükséges.

`Iterable` vs `Collection` vs `List`

Iterable → iterálható (for-each)
  Collection → + size, contains, add, remove
    List → + index elérés, rendezés

Minden szint overhead-et ad (memória, komplexitás) — válaszd a minimális szerződést.

`Collections.unmodifiableXxx` vs `List.of` / `Set.of`

  • Collections.unmodifiableList(lista) — egy mutable lista nézete; az alap változásai láthatóak!
  • List.of(...) (Java 9+) — valóban immutable; minden mutáló hívásra kivételt dob
  • List.copyOf(collection) (Java 10+) — immutable másolat, pillanatkép szemantika

A teljesítmény és az interfész-választás kapcsolata

Egy LinkedList átadása olyan metódusnak, amely ismételten hívja a get(int)-et, O(n²) komplexitást eredményez. A List<T> elfogadása az API-ban nem garantálja a véletlenszerű elérést — fontolja meg a RandomAccess marker interfészt vagy dokumentálja a követelményeket.

`SortedSet` és `SortedMap` konzisztencia

A comparator legyen konzisztens az equals-szal: ha comparator.compare(a, b) == 0, akkor a.equals(b) is true kell legyen. Különben a Set szerződés (nincs duplikátum) csendesen megsérül.

Az élő nézet nem immutábilis pillanatkép

List<String> forras = new ArrayList<>(List.of("a", "b"));
List<String> nezet = Collections.unmodifiableList(forras);

forras.add("c");
System.out.println(nezet); // [a, b, c]

Ez egy csak olvasható wrapper, nem fagyasztott másolat.

A szűkebb visszatérési típus csökkenti a csatolást

Ha a hívónak csak iterálnia kell, sokszor őszintébb Iterable<T>-t visszaadni, mint List<T>-et. Minél szűkebb a szerződés, annál könnyebb később implementációt cserélni.


8. Interjúkérdések

Miért nem terjeszti ki a `Map` a `Collection`-t?

Mert a map nem egyszerű elemek halmaza, hanem kulcs-érték asszociációkat modellez, eltérő műveletekkel és szerződéssel.

Mikor adnál vissza `Iterable`, `Collection` vagy `List` típust?

A legszűkebb típust add vissza, ami még kiszolgálja a hívó igényeit: Iterable bejárásra, Collection méretre és tagságra, List pedig akkor, ha sorrend és indexelés is része a szerződésnek.

Mi a gyakorlati különbség a `Queue` és a `Deque` között?

A Queue egyvégű head/tail feldolgozást modellez, a Deque viszont mindkét végén hatékony műveleteket enged, ezért stack-helyettesítőként is használható.

Miért „veszíthet el" egy `TreeSet` látszólag különböző értéket?

Mert az egyediséget a rendezési logika dönti el. Ha a comparator szerint két elem 0-val hasonlítható össze, a set ugyanannak a helynek tekinti őket.


9. Szószedet

Fogalom Definíció
JCF Java Collections Framework — a gyűjtemény interfészek és implementációk egységes hierarchiája
Szerződés Az a viselkedési garancia, amelyet egy interfész biztosít a hívóinak
RandomAccess Marker interfész ArrayList-en stb., jelzi az O(1) index-elérést
FIFO First-In-First-Out — a beszúrás sorrendje határozza meg a törlés sorrendjét (Queue)
LIFO Last-In-First-Out — a legutóbb hozzáadott elem kerül először törlésre (Stack/Deque)
NavigableSet SortedSet kiterjesztése floor/ceiling/lower/higher lekérdezésekkel
Fail-fast iterator Iterator, amely ConcurrentModificationException-t dob, ha a gyűjtemény módosul iterálás közben
Strukturális módosítás Bármely változás a gyűjtemény méretén (add/remove), szemben a set-tel

10. Cheatsheet

  • 📋 List — rendezett, index-elérés, duplikátumok OK → ArrayList (alapértelmezett)
  • 🔑 Set — nincs duplikátum, gyors containsHashSet (alapértelmezett)
  • 🗺️ Map — kulcs-érték, gyors get kulcs alapján → HashMap (alapértelmezett)
  • 📬 Queue — FIFO feladatfeldolgozás → ArrayDeque (ajánlott a LinkedList helyett)
  • ↔️ Deque — mindkét vég elérhető, stack is → ArrayDeque
  • ⚠️ Stack osztály legacy — használj ArrayDeque-t helyette
  • Interfész ellen programozzList<E>, ne ArrayList<E> a szignatúrákban
  • 🔒 Immutable gyűjteményekList.of(), Set.of(), Map.of() (Java 9+)
  • 🚫 TreeSet/TreeMap visszautasítja a null kulcsokat/elemeket
  • 🔄 Módosítás iterálás közbeniterator.remove() vagy removeIf()

🎮 Játékok

10 kérdés