Obsah
Kontejnery jako základní dynamické struktury v Javě
Kolekce, iterátory (Collection, Iterator)
Seznamy (rozhraní List, třídy ArrayList, LinkedList)
Množiny (rozhraní Set, třída HashSet), uspořádané množiny (rozhraní SortedSet, třída TreeSet), rozhraní Comparable, Comparator
Mapy (rozhraní Map, třída HashMap), uspořádané mapy (rozhraní SortedMap, třída TreeMap)
Klasické netypové vs. nové typové kontejnery - generické datové typy
Iterace cyklem foreach
Starší typy kontejnerů (Vector, Stack, Hashtable)
Kontejnery (containers) v Javě
slouží k ukládání objektů (ne hodnot primitivních typů!)
v Javě koncipovány dosud jako beztypové - to se ve verzi 1.5 částečně změní!
tím se liší od např. Standard Template Library v C++
Většinou se používají kontejnery hotové, vestavěné, tj. ty, jež jsou součastí Java Core API:
vestavěné kontejnerové třídy jsou definovány v balíku
java.util
je možné vytvořit si vlastní implementace, obvykle ale zachovávající/implementující „standardní“ rozhraní
Funkcionalita vestavěných kontejnerů je obvykle předepsána výhradně rozhraním, jenž implementují.
Rozhraní však připouštějí, že některé metody jsou nepovinné, třídy jej nemusí implementovat!
V praxi se totiž někdy nehodí implementovat jak čtecí, tak i zápisové operace - některé kontejnery jsou „read-only“
Moderní kontejnery jsou nesynchronizované, nepřipouštějí souběžný přístup z více vláken.
Standardní, nesynchronizovaný, kontejner lze však „zabalit“ synchronizovanou obálkou.
Při práci s kontejnery může vzniknout řada výjimek, např. IllegalStateException apod.
Většina má charakter výjimek běhových, není povinností je odchytávat - pokud věříme, že nevzniknout.
Iterátory jsou prostředkem, jak "chodit" po prvcích kolekce buďto
Každý iterátor musí implementovat velmi jednoduché rozhraní Iterator se třemi metodami:
jsou kontejnery implementující rozhraní Collection - API doc k rozhr. Collection
Rozhraní kolekce popisuje velmi obecný kontejner, disponující operacemi: přidávání, rušení prvku, získání iterátoru, zjišťování prázdnosti atd.
Mezi kolekce patří mimo Mapy všechny ostatní vestavěné kontejnery - List, Set
Prvky kolekce nemusí mít svou pozici danou indexem - viz např. Set
lineární struktury
implementují rozhraní List (rozšíření Collection) API doc k rozhr. List
prvky lze adresovat indexem (typu int)
poskytují možnost získat dopředný i zpětný iterátor
lze pracovat i s podseznamy
jsou struktury standardně bez uspořádání prvků (ale existují i uspořádané, viz dále)
implementují rozhraní Set (což je rozšíření Collection)
Cílem množin je mít možnost rychle (se složitostí O(log(n))) provádět atomické operace:
Implementují rozhraní SortedSet -API doc k rozhraní SortedSet
Jednotlivé prvky lze tedy iterátorem procházet v přesně definovaném pořadí - uspořádání podle hodnot prvků.
Existuje vestavěná impl. TreeSet - černobílé stromy (Red-Black Trees) API doc ke třídě TreeSet
Mapy (asociativní pole, nepřesně také hašovací tabulky nebo haše) fungují v podstatě na stejných principech a požadavcích jako Set:
Implementují rozhraní SortedMap - API doc k rozhraní SortedMap
Dvojice (klíč, hodnota) jsou v nich uspořádané podle hodnot klíče.
Existuje vestavěná impl. TreeMap - černobílé stromy (Red-Black Trees) - API doc ke třídě TreeMap
Uspořádání lze ovlivnit naprosto stejným postupem jako u uspořádané množiny.
Jsou-li klíče uspořádané (pomocí implementace Comparable nebo komparátorem), mohou se prvky procházet podle uspořádání klíčů:
Příklad, kdy jsou klíče uspořádané komparátorem:
Obrázek 15. Vložení účtů do mapy pod uspořádaným klíčem člověka - vlastníka
package tomp.ucebnice.kolekce;
import java.util.*;
public class SortedMapComparatorDemo {
public static void main(String[] args) {
// vytvoříme mapu
SortedMap sm = new TreeMap(new ClovekComparator());
// naplníme ji třemi lidmi
Clovek c1 = new Clovek("Josef", "Vykoukal");
Ucet u1 = new Ucet(100);
sm.put(c1, u1);
Clovek c2 = new Clovek("Dalimil", "Brabec");
Ucet u2 = new Ucet(50);
sm.put(c2, u2);
Clovek c3 = new Clovek("Viktor", "Kotrba");
Ucet u3 = new Ucet(2000);
sm.put(c3, u3);
// projdi abecedně všechny vlastníky účtů v mapě
// proto je třeba získat iterátor nad množinou klíčů
for(Iterator i = sm.keySet().iterator(); i.hasNext(); ) {
Clovek majitel = (Clovek)i.next();
Ucet ucet = (Ucet)sm.get(majitel);
majitel.vypisInfo();
System.out.println(" je majitelem uctu se zustatkem "
+ ucet.zustatek + " Kc");
}
}
static class Ucet {
double zustatek;
public Ucet(double z) {
zustatek = z;
}
}
static class Clovek { // nemusí být Comparable
String jmeno, prijmeni;
Clovek (String j, String p) {
jmeno = j;
prijmeni = p;
}
public void vypisInfo() {
System.out.print("Clovek "+jmeno+" "+prijmeni);
}
}
static class ClovekComparator implements Comparator {
public int compare(Object o1, Object o2) {
// porovnává jen lidi a to podle příjmení
if (o1 instanceof Clovek && o2 instanceof Clovek) {
Clovek c1 = (Clovek)o1;
Clovek c2 = (Clovek)o2;
return c1.prijmeni.compareTo(c2.prijmeni);
} else
throw new IllegalArgumentException(
"Nelze porovnat objekt typu Clovek s objektem jineho typu");
}
}
}
na bázi pole (ArrayList) - rychlý přímý přístup (přes index)
na bázi lineárního zřetězeného seznamu (LinkedList) - rychlý sekvenční přístup (přes iterátor)
téměř vždy se používá ArrayList - stejně rychlý a paměťově efektivnější
Demo efektivity práce kontejnerů - Demo kolekcí
Velmi podrobné a kvalitní seznámení s kontejnery najdete na Trail: Collections