IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

2. diel - Java Collections Framework

V minulej lekcii, Úvod do kolekcií a genericita v Jave, sme si urobili úvod do kolekcií a ukázali sme si, čo je to genericita.

V dnešnej lekcii si povieme, ako má jazyk Java kolekcie implementovanej. Predstavíme si základnú časť z Java Collections Frameworku.

Java Collections Framework

Každý dobrý programovací jazyk ponúka v štandardnej knižnici prácu s kolekciami. V jazyku Java túto časť rieši celý framework nazvaný Java Collections Framework. Ide o relatívne zložitú hierarchiu rozhrania a tried, ktoré sú dostupné všetkým programátorom. Základná vizualizácia tohto frameworku je vidieť na UML diagrame nižšie:

Zjednodušený diagram Java Collections Frameworku - Kolekcie a prúdy v Jave

Základné rozhranie, ktoré zaisťuje každú kolekciu v Jave, je rozhranie Collection. Toto rozhranie popisuje základné metódy pre prácu s každou kolekciou. Výber najdôležitejších metód je k dispozícii na obrázku nižšie:

Výpočet najdôležitejších metód v rozhraní Collection - Kolekcie a prúdy v Jave

Teraz si tieto metódy popíšeme:

  • size() - vráti aktuálny počet prvkov v kolekcii
  • isEmpty() - vráti true, pokiaľ sa v kolekcii nenachádza žiadny prvok, inak false
  • contains() - vráti true, pokiaľ kolekcia obsahuje prvok z parametra
  • add() - pridá prvok do kolekcie; vráti true, ak sa zmenila kolekcia (prvok bol pridaný), inak false
  • remove() - odoberie prvok z kolekcie; vráti true, ak sa zmenila kolekcia (prvok existoval a bol odobraný), inak false
  • clear() - vymaže obsah kolekcie

Rozhranie Collection rozširuje rozhranie Iterable. Toto rozhranie definuje metódy na prehliadanie nielen kolekcií, ale všetkých objektov, nad ktorými je možné iterovať. Rozhranie obsahuje metódu iterator(), ktorú musia implementovať všetky kolekcie. Tá vracia tzv. iterátor, ktorý si hneď vysvetlíme. Ďalej rozhranie obsahuje dve default metódy s implementáciou: forEach() a spliterator(), ktorým sa budeme venovať v ďalších lekciách.

Iterátor

Iterátory sú objekty, ktoré slúžia na prehliadanie kolekcií. Iterátor sme vlastne už použili bez toho, aby sme o tom vedeli, a to pri kolekcii ArrayList.

Priechod cez indexy

Keď sme prechádzali poľa, ktoré nie je plnohodnotnou kolekciou, mali sme na výber dve konštrukcie: cez indexy pomocou cyklu for:

String[] names = new String[] {"Kyle", "Peter", "Michael", "John"};

for (int i = 0; i < names.length; i++) {
    System.out.println(names[i]);
}

A pomocou foreach:

String[] names = new String[] {"Kyle", "Peter", "Michael", "John"};

for (String name: names) {
    System.out.println(name);
}

Keď použijeme foreach nad jednoduchým poľom, Java interne rovnako použije prístup cez indexy. Foreach je len tzv. syntax sugar, krajšia syntax pre programátora, ktorá sa ešte pred kompiláciou automaticky nahradí iným, typicky zložitejším kódom.

Priechod kolekcií iterátorom

Na prechádzanie skutočných kolekcií, teda zložitejších štruktúr ako je pole, napr. ArrayList, môžeme tento syntaktický cukor využiť úplne rovnako. Len Java interne použije tzv. iterátor a náš kód sa vnútorne preloží na niečo také:

List<String> lastName = new ArrayList<>();
for (Iterator<String> iterator = lastName.iterator(); iterator.hasNext(); ) {
    String next = iterator.next();
    System.out.println(next);
    iterator.remove(); // If the collection supports it, the current element is deleted
}

Znalosť iterátorov sa nám v praxi oplatí v prípade, keď budeme chcieť počas prehliadania z kolekcie mazať. Vtedy ich musíme na prechádzanie explicitne použiť, viď ďalej. Ďalšie využitie iterátora je pre naše vlastné kolekcie, na ktoré následne pôjde používať foreach cyklus.

Rozhranie Iterator

Na chvíľu sa zastavíme pri rozhraní Iterator, ktoré je vrátené rovnomennou metódou. Toto rozhranie obsahuje dve dôležité metódy: next() a hasNext(). Metódy si opäť popíšme:

  • next() - vráti nasledujúci prvok
  • hasNext() - vráti true, ak existuje nasledujúci prvok

Pomocou týchto 2 metód je Java následne schopná kolekciu od začiatku do konca prejsť.

Od Javy verzie 8 sú na rozhraní tiež metódy:

  • remove() - odstráni prvok z kolekcie, pokiaľ túto operáciu kolekcie podporuje, inak sa vyvolá výnimka UnsupportedOperationException ; toto je jediný správny spôsob, ako sa dá odstrániť prvok z kolekcie, keď ňou prechádzame
  • forEachRemaining() - prejde každý prvok kolekcie a aplikuje naň príslušnú akciu

Vlastný iterátor

Ukážme si ako implementovať vlastný iterátor, teda objekt umožňujúci prechod nejakou kolekciou. Uvažujme, že sme si vytvorili vlastnú kolekciu SimpleList, ktorá len obaľuje obyčajné pole, ktoré ju príde v konštruktore. Triede nebudeme pridávať žiadne metódy, iba ju implementujeme rozhranie Iterable a metódu iterator(), ktorá vráti anonymnú implementáciu rozhrania iterátor:

public class SimpleList<Type> implements Iterable<Type> {

    private Type[] arrayList;
    private int currentSize;

    public SimpleList(Type[] newArray) {
        this.arrayList = newArray;
        this.currentSize = arrayList.length;
    }

    @Override
    public Iterator<Type> iterator() {
        Iterator<Type> it = new Iterator<Type> () {

            private int currentIndex = 0;

            @Override
            public boolean hasNext() {
                return currentIndex < currentSize && arrayList[currentIndex] != null;
            }

            @Override
            public Type next() {
                return arrayList[currentIndex++];
            }
        };
        return it;
    }
}

Trieda SimpleList prijme v konštruktore poľa, nad ktorým sa bude vytvárať iterátor. Je dôležité, aby volanie metódy iterator() vždy vrátilo novú inštanciu triedy Iterator. Iterátor je možné použiť iba na prechádzanie kolekcie od začiatku do konca. Ak chceme iterovať odzadu, treba najskôr vytvoriť kolekciu, ktorá bude prevrátená a až nad ňou vytvoriť nový iterátor. V metóde hasNext() zisťujeme, či môže iterátor vrátiť ďalší prvok, alebo už došiel nakoniec. Metódou next() vrátime aktuálny prvok a zvýšime index poľa.

Všimnite si, že sme rozhranie Iterator implementovali ako anonymnú triedu. Samozrejme by sme si aj mohli deklarovať plnohodnotnú triedu, napr. SimpleIterator, a v metóde iterator() vracať jej inštanciu.

Potomkovia Collection

Rozhranie Collection je rozšírené o metódy podľa spôsobu použitia pomocou rozhrania List, Set a Queue. Úplne samostatne leží rozhranie Map, ktoré obsahuje metódy pre prácu s kolekciami typu "kľúč - hodnota". Základné metódy týchto rozhraní sú implementované v abstraktných triedach podľa typu rozhrania: AbstractList, AbstractSet, AbstractQueue a AbstractMap. Abstraktné triedy sú tu použité, pretože niektoré konkrétne implementácie rozhrania môžu zdieľať implementáciu základných metód (size(), isEmpty()), ale budú mať rozdielne metódy ako je add(), remove(). Ďalej sú tieto abstraktné triedy užitočné v prípade, že si budete chcieť implementovať vlastnú kolekciu, ale chcete mať už nejaký základ implementovaný.

Aby som bol úplne presný, tak všetky vyššie vymenované abstraktné triedy okrem AbstractMap ešte dedia od spoločnej abstraktnej triedy AbstractCollection. Všetky triedy možno nájsť v balíčku java.util. Tieto triedy majú jednu spoločnú vlastnosť: nie sú thread-safe. To znamená, že nemajú zabezpečenie pre modifikáciu prvkov z viacerých vlákien. Tento problém je v Jave riešený pomocou tried, ktoré sa nachádzajú v balíčku java.util.concurrent. Tu sa okrem iného nachádzajú rovnomenné triedy s podporou modifikácie z viacerých vlákien. Napríklad pre ArrayList tu existuje thread-safe verzia v podobe CopyOnWriteArrayList.

V ďalších lekciách postupne preberieme najdôležitejšie rozhranie List, Set, Queue a Map a ich implementácie, konkrétne ArrayList, LinkedList, HashSet a HashMap.

V budúcej lekcii, Zoznam (List) pomocou poľa v Jave, sa bližšie pozrieme na kolekciu List, predstavíme si rôzne implementácie tejto kolekcie a ich výhody a nevýhody..


 

Predchádzajúci článok
Úvod do kolekcií a genericita v Jave
Všetky články v sekcii
Kolekcie a prúdy v Jave
Preskočiť článok
(neodporúčame)
Zoznam (List) pomocou poľa v Jave
Článok pre vás napísal Petr Štechmüller
Avatar
Užívateľské hodnotenie:
2 hlasov
Autor se věnuje primárně programování v Javě, ale nebojí se ani webových technologií.
Aktivity