Java Source

Sunday, December 03, 2006

Kolekce v jazyku Java

Kolekce v programovacím jazyku Java slouží k ukládání, procházení (výběr) a mazání prvků. Java poskytuje několik typů kolekcí pro tuto funkčnost. Všechny však mají něco společného, mají společné rozhraní java.util.Collection (snad až na Map, TreeMap, SortedMap a příbuzné). Collection definuje základní metody pro práci nad kolekcemi.

Collection je základní rozhraní, od kterého dědí všechny ostatní rozhraní pro kolekce. Jaké třídy implementují rozhraní Collection můžeme nalézt v Java API (http://java.sun.com/j2se/1.5.0/docs/api/).

Když porovnáte kolekce v Java 5 s kolekcemi v Java 1.4, tak zásadní změna je v obohacení o parametrizované typy. Dále přibyly další kolekce.

Ve starších verzích Javy (verze 1.1) byla dostupná pouze kolekce Vector. Vector se dal procházet buď indexově (jako pole) nebo pomocí Enumeration. V několika knihách jsem se dočetl, že Vector je zastaralý a neměl by se používat. Měl by se, podle nich, na místo Vector použít ArrayList. Abych Vector neoprávněně neodsuzoval, udělal jsem si malý výkonnostní test a vyšlo mi, že Vector je pomalejší od ArrayList o nějakých cca 10%. Není to ovšem vina špatných algoritmů, nebo špatné implementace. Je to proto, že Vector je serializovaný a ArrayList nikoli. Proto Vector není nijak zastaralý a odsouzený k zániku. Je nevýkonný tehdy, když ho použijete na špatném místě. Jak to bývá ostatně kdykoli, když se používají nesprávné postupy, když člověk neví co přesně a proč dělá. Kdyby ho autoři jazyka nechtěli, tak by ho snad daly alespoň deprecated.
Proto řeči "Já mám raději ArrayList, protože je novější, Vector je zastaralý a pomalý!" jsou neopodstatněné.

Od verze 1.2 byly do Javy přidány další kolekce a byla rozšířena jejich funkčnost.

Základní rozdělení v rozhraní (s příslušnými podtřídami):
1. Collection
1.1 List (ArrayList, Vector, Stack)
1.2 Set (HashSet, LinkedHashSet)
1.2.1 SortedMap (TreeSet)
1.3 Queue (LinkedList)
2 Map (SortedMap, HashMap, LinkedHashMap, Hashtable, WeakHashMap)
2.1 SortedMap (TreeMap)

Seznam není úlpný a ani není třeba ho ani doplňovat. Například v seznamu chybí kolekce pro Enum, ale o těch bych se chtěl rozepsat někdy jindy a podrobněji. Pro to, jaké kolekce nám Java poskytuje se stačí podívat do API.

Nyní si popíšeme a ukážeme vhodné použití kolekcí. Při popisu nebudu vypisovat všechny metody, které kolekce poskytují. Budu se více zaměřovat na funkcionalitu, vhodné použití a optimalizaci.

1. ArrayList
ArrayList je jedna ze základních kolekcí. Je to třída, která implementuje rozhraní List. Jednoduše se dá říct, že je to dynamické (nafukovací) pole. O bufferování ArrayListu se můžeme prosadit sami, nebo to nechat na automatické správě bufferu ArrayListu.
Pokud chceme a víme, že budeme potřebovat vložit do ArrayList 10 000 prvků hned při vložení, ArrayList patřičně inicializujeme: new ArrayList(10000). Pokud nevíme kolik prvků bude vloženo do kolekce (a víme, že jich moc nebude) inicializujeme třídu ArrayList výchozím postupem: new ArrayList().

Příklad: Při vytvoření ArrayList se inicializuje pole o velikosti například 1 000. Až vyplníme kolekci (pole), vytvoří se jiné větší pole, do kterého se překopíruje obsah předchozího pole. A můžeme pokračovat v plnění. Jde vidět, že se ArrayList chová jako StringBuffer.

Pokud bychom chtěli aplikaci optimalizovat a vyladit práci s kolekcemi, můžeme si napsat malý statistický program. Ten by nám zjišťoval jaká je počáteční velikost, velikost po naplnění a velikost po některém dalším přidání prvků. Z toho bychom viděli a měli přesný přehled s jakou hodnotou inicializovat ArrayList.

Další věc, kterou bychom měli dodržovat a každé rozumné IDE to vyžaduje, je vytvářet typově bezpečné kolekce. Když nebyly generické typy (parametrizované typy, šablony, ...) vytvářely se třídy, které zaobalily kolekcí a nedovolily nikomu vložit do kolekce dva nebo více různých typů.
Zjednodušeně něco jako toto:

class StringArrayList {
private List list = new ArrayList();
public void add(String s) {
list.add(s);
}
public String get(int i) {
return (String) list.get(i);
}
}

S použitím generických typů se všechno zjednodušuje a odpadá nutnost implementovat tuto třídu. Typově bezpečnou kolekci vytvoříme takto:
List <String> list = new ArrayList <String>()
.
O tom co to znamená jsem už něco napsal, tak se na to teď odkazuji.

2. Vector
Vector má stejně jako ArrayList stejné předky. Liší se od ArrayListu tím, že je synchronizovaný.

3. Stack
4. HashSet
5. LinkedHashSet
6. TreeSet
7. LinkedList
8. SortedMap
9. HashMap
10. LinkedHashMap
11. Hashtable
12. WeakHashMap
13. TreeMap

Friday, December 01, 2006

Generické DAO

Nač použít generické typy? Tak například pro vytvoření generického DAO návrhového vzoru.
Když napíšeme DAO pomocí generických typů, můžeme pak ukládat jakékoli třídy. Samozřejmě že předním musíme nějak pro databázi namapovat. To by ovšem mělo být v moci daného DAO.

Nejdříve vytvoříme interface, a nadefinujeme jak naše DAO objekty budou vypadat. Dále přidáme generické typym kde "T" bude představovat ukládaný objekt, a "N" bude id (klíč) k danému objektu.
public interface GenericDao <T, N extends Serializable> {
N create(T o);
T read(N id);
void update(T o);
void delete(T o);
}


Po té co je interface hotov tak můžeme začít s jeho implementací. Následující implementace bude pro Hibernate. Samozřejmě že to můžeme naimplentovat jak chceme, a tady je jeden ze způsobů.
public class GenericDaoHibernate <T, N extends Serializable> implements GenericDao <T, N> {

private Class type;

public GenericDaoHibernateImpl(Class type) {
this.type = type;
}
public N create(T o) {
return (N) HibernateUtil.getSession.save(o);
}
public T load(N id) {
return (T) HibernateUtil.getSession.load(type, id);
}
public void update(T o) {
HibernateUtil.getSession.update(o);
}
public void delete(T o) {
HibernateUtil.getSession.delete(o);
}
}


Jde vidět, že používáme třídu HibernateUtil, ta slouží pro poskytování přístupu do databáze. Ve skutečnosti jen poskytuje Session, která nám umožňuje pracovat s databází.

Jak jde vidět generické typy při vhodném použití poskytují mocný nástroj pro vývoj.

Generické typy

Když jsem si stáhnul a nainstaloval Java 5 na svůj počítač, byl jsem zvědavý jaké nové funkčnosti nám tato nová verze přinese. Okamžitě jsem si koupil knížku od Rudolfa Pecinovského, "Java 5.0 - Novinky jazyka a upgrade aplikací" a pustil jsem se do objevování co nového je.

Zajimavé jsou všechny nové funkčnosti. Až snad na enum, ty nepracují tak jak mají a když se snažíte o serializaci, tak se Vám nepodaří nalézt nastavené hodnoty. Zkrátka enum v Jave pořádně nefungují. Proto budeme muset jestě nějakou dobu používat vlastnoruční enumy.

O čem jsou generycké typy? Jednodušše řečeno, generické typy slouží pro vytváření abstraktních znovu použitelných typů. Tím se stává třída velmi mocná. Využít toho lze pro vytváření znovu použitelných tříd (například lze vytvořít "Generické DAO") . Dálší z hlavních funkčností je vytvoření typově bezpečných kolekcí.

Jak lze využít generických typů pro zajištění typově bezpečných kolekcí můžeme vidět v následujícím kódu.

Tato konstrukce nám zajistí typovou bezpečnost kolekce, to znamená, že pokud se pokusíme přidat do těchto kolekcí něco jiného něz jsme nadefinovaly, nepodaří se nám to ani zkompilovat natož spustit. Následující výpis kódu ukazuje jak toto vhodně použít.
List<String> stringList = new ArrayList<String>();
stringList.add("retezec");


Zde je ukázka pokusu vložit objekt jiného typu do typově bezpečné kolekce. Tato snaha skončí neuspěšným pokusem o přeložení.
stringList.add(new Integer(0));

Co se děje na pozadí při vložení objektu správného typu? Překladač, jednoduše řečeno, udělá následné.
List stringList = new ArrayList();
stringList.add("retezec");

Nebo by se dalo říct, že jen ořeže kolekci od generických typů. A proč? Hlavní je zajistit, aby programor nevědomky (nebo snad ne umyslně) neplnil kolekci jiným typem. Doposud to šlo řešit tak že se vytvořila třída, která zaobalila kolekci svými metodami a zajistila tak typovou bezpečnost, zatím co kolekce byla pro okolí neviditelná.

Pro to, co lze nyní napsat jedním řádkem se musela napsat celá třída. Zjednodušeně by se dalo říct, že jde v tomto případě jen pro programátorovo pohodlí.