Google collections - ušetřete si práci s kolekcemi

Nedávno mě při poslechu JavaPosse zaujala zmínka o Google Collections. Jedná se o knihovnu doplňující funkcionalitu třídy Collections ze standardní Javy. Knihovna obsahuje řadu utility tříd, které zpříjemňují život s generikami v kolekcích, vytváření kolekcí v kolekcích a další manipulaci dat v kolekcích. Jelikož mě knihovna zaujala hned na první pohled, rozhodl jsem se podívat se jí na zoubek a podělit se s vámi o své zkušenosti.

Zkrácení zápisu pro vytvoření nových kolekcí s generikami

Jedná se možná o drobnost, ale natolik častou, že i drobné vylepšení přinese celkové zpřehlednění zápisu a zrychlení práce. V klasickém Java 1.5 kódu při vytváření generické kolekce typicky píšete např.:

Na pravé straně deklarace tedy opakujete generickou informaci - a zbytečně - tato informace se dá přeci odvodit z levé strany přiřazení. Java bohužel tuto informaci vyžadujete, protože pokud byste zadali jednoduše new HashMap(), vytvoříte obyčejnou mapu bez informace o generikách a Java vás bude při kompilaci otravovat hlášením: Unchecked assignment.

Google collection obsahují řadu statických metod pro vytváření standardních kolekcí, které vás zbaví nutnosti opakovat informaci o generikách při vytváření těchto kolekcí. Stejnou mapu jako v předchozím příkladě vytvoříme napsáním:

Podobných metod je ve třídě Maps řada (namátkou třeba newLinkedHashMap, newConcurrentHashMap, newTreeMap). Podpora existuje nejen pro mapy, ale i pro další typy kolekcí:

Konverze polí objektů a primitivních typů

Všichni známe třídu Arrays ze standardní Javy. Ta sice obsahuje velmi užitečné funkce, ale zakrátko člověk zjistí, že si stejně řadu věcí neustále dopisuje sám. Jsou to drobnosti na tři řádky, ale stokrát nic umořilo osla. Google collections nabízí právě tyto drobnosti, které šetří práci.

Práce s poli objektů

Jednoduše je možné slučovat pole objektů. Dříve bylo nutné manipulovat s System.arraycopy - díky knihovně je možné jednoduše zapsat:

Konverze polí na list a zpět

Pracovat s poli v Javě má řadu výhod. Kód pro práci s nimi je velmi úsporný a přehledný. Pokud mám seznam objektů, který má být od počátku inicializován, rád používám čitelný zápis inicializace seznamu jako pole:

Pokud se ovšem stane, že do pole chci přidat další záznam, musím data vložit do listu, jehož inicializace je již na několik řádků a přehlednost se ztrácí. V google collections jsou metody, které tuto práci šetří a zvyšují tak subjektivní použitelnost polí jako takových:

Pokud by pro mě byla čitelnost zápisu pro vytvoření seznamu jediným měřítkem, vyplatí se mi použít jinou metodu z nabídky google collections:

Multimapy

Snad každý z nás potřebuje a často vytváří mapy, které v hodnotách obsahují nikoliv jediný objekt, ale seznam objektů reprezentovaný např. ArrayListem. Já sám to programoval už asi stokrát - není to moc kódu, takže mě nikdy nenapadlo to přesunout do knihovny, ale zase se to používá tak často, že se to ve výsledku opravdu vyplatí. V daném případě musí totiž člověk ošetřit situaci vložení prvního objektu pod daným klíčem - list totiž neexistuje a proto je třeba vytvořit nejdříve list, do něj vložit prvek a teprve tento list vložit pod klíčem do mapy. V google collections lze jednoduše třebas takto:

Výsledkem bude očekávaný list stringů obsahující ahoj a ciao. Google collections obsahují řadu implementací pro tyto Multimapy:

Podpora pro Decorator pattern

Google poskytuje základní implementace wrapperů nad standardními kolekcemi a iterátory. Zjednodušují tak práci pro implementaci decorator patternu. Stačí vám jen jednoduše podědit z těchto základních Forwarding class a přetížit konkrétní metody, jejichž chování si přejete změnit. Pro přístup k původnímu (backing) objektu slouží metoda delegate().

Podmínky pro záznamy v kolekcích

Tato podkapitolka a ta následující mi hodně připomíná použití Closures. Umožňuje jednoduše kontrolovat vkládání nových záznamů do kolekcí. Víte, že list vám umožňuje bez problémů vytvořít záznam s null hodnotou. To může vést k chybám, které nejsou na první pohled úplně patrné. S google collection se dá tomuto případu jednoduše zabránit:

Constrainty si samozřejmě můžete implementovat vlastní. Jsou aplikovány před vložením nového záznamu do kolekce a jednoduše tak můžete ohlídat hodnoty, které se v listu nachází. To podstatně zvyšuje míru důvěry, kterou takové kolekci ve svém kódu můžete dát.

Filtrování hodnot v iterátoru

Velmi elegantně a čitelně lze také filtrovat hodnoty, které poskytuje iterátor nad kolekcí.

Predikáty je možné slučovat pomocí and, or nebo negovat jako ve výše uvedeném příkladě. Predikáty je možné volně vytvářet. Čitelnost kódu je daleko lepší než ifování v iteraci. Nyní je predikát definován jako rozhranní s jedinou metodou - ideální adept na zavedení closure.

Třída iterators obsahuje řadu dalších užitečných metod:

  • addAll(Collection collection, Iterator iterator) - vloží všechny prvky iterátoru do kolekce
  • all(Iterator iterator, Predicate predicate) - vrátí true, pokud všechny prvky iterátoru odpovídají podmínce
  • any(Iterator iterator, Predicate predicate) - vrátí true, pokud alespoň jeden prvek odpovídá podmínce
  • concat(….) - různé metody pro slučování více iterátorů do jediného
  • find(Iterator iterator, Predicate predicate) - vrátí první prvek iterátoru odpovídající podmínce
  • a další ;-)

Maven

Bohužel knihovna není buildována Mavenem, takže si budete muset zajistit upload do vaší firemní repository sami. Pozitivní je, že nevyžaduje žádné další dependence, takže se jedná o velmi jednoduchou operaci.

Závěrem

Google collections je knihovna obsahující plno drobných vylepšení, funkcí a utilitek, bez kterých se sice obejdete, ale které vám mohou ušetřit plno řádků kódu (a to nemluvím o kódu testujících váš kód) a které mohou zásadně přispět k zpřehlednění kódu jako takového. Knihovnu využijete až od Javy 1.5, jelikož hlavní podpora je spojená právě s generikami. Knihovnu můžu jen doporučit a díky pánům z JavaPosse za dobrý tip a Googlu za open source.

BTW: nemáte někdo pár akcií na prodej? :-)

Související odkazy

Podělte se s ostatními:
  • Digg
  • del.icio.us
  • De.lirio.us
  • Technorati
Ohodnoťte článek:
Takovéhle články už radši ne!Nic nového pod sluncem.Průměr - obsahuje zajímavé střípky informací.Hodnotný článek - lecos nového jsem se dozvěděl.Skvělý článek - informace se mi dost hodí. (9 hlasů, průměrně: 4.44 z 5)
Loading ... Loading ...

9 reakcí to “Google collections - ušetřete si práci s kolekcemi”

  1. Tomas Kutin:

    zajimave, pekne, uzitecne : ) libi se mi to!

  2. ufak:

    A co Appache commons?

    To jsem netusil, ze prekladac se kouka na navratovy typ pri volani
    To je sice elegantni, zajimave, da se to vyuzit i natlouct si nos.

    public static HashMap newHashMap() {
    return new HashMap();
    }

    Dokonce zbastil i

    Map<String, java.util.List> mSL = newHashMap();
    java.util.List list1 = new java.util.ArrayList();
    list1.add( “ahoj”);
    list1.add( “nazdar”);
    mSL.put( “prvni”, list1 ) ;

  3. Novoj:

    No Google Collections se mi zdá, že na Apache Commons hodně ideově navazuje. Problém s Apache Commons je ten, že nejsou updatované pro generiky. Výše uvedený zápis kódu, který jsi poslal mi hlásí. “Unchecked assignment” - což je kompilační warning, který s Google collections nemám. Navíc právě to druhé co jsi napsal se dá elegantně řešit multimapou.

  4. ZVrablik:

    No a pokud pouzivate javu 1.4 muzete zkusit neco starsiho, ale docela dobre pracujiciho: http://pcj.sourceforge.net/

  5. alfonz:

    “Problém s Apache Commons je ten, že nejsou updatované pro generiky.” … http://collections15.sourceforge.net/

    jinak některé věci jsou zajímavé, to rozhodně ano, ale zdá se mi že za některými je skryt příliš pomalý kód. Usnadnění vytváření kolekcí je super, ale nenapadá mne jak to udělat jinak než přes reflexi a to je prostě o dost pomalejší.

  6. Novoj:

    TO: alfonz

    Na odkazovaném linku jsem ani nenašel jak knihovnu pořádně stáhnout. Možná se to spíše konvertovalo na projekt: http://larvalabs.com/collections/

    Každopádně, co se týká výkonnosti, dovolil bych si nesouhlasit … schválně, tady je kód pro vytvoření generické mapy:

    public static <K, V> HashMap<K, V> newHashMap() {
    return new HashMap<K, V>();
    }

    Zde žádnou reflection nevidím. Ani v multimapách a dalších věcech, když jsem procházel kódem, jsem na žádnou reflexi nenarazil. Na jedinou alchymii jsem narazil v ObjectArrays a to:

    public static <T> T[] newArray(Class<T> type, int length) {
    return (T[]) Array.newInstance(type, length);
    }

    kde pod Array.newInstance se skrývá:

    private static native Object newArray(Class componentType, int length)
    throws NegativeArraySizeException;

    Což je už ale standardní Java používající nativní implementaci. Takže s druhou částí tvého tvrzení se mi nechce souhlasit.

  7. Ladislav Thon:

    > nenapadá mne jak to udělat jinak než přes reflexi

    Typová inference.

    Map<Integer, StringBuffer> bufferIndex = Maps.newHashMap();

    je úplně totéž, jako

    Map<Integer, StringBuffer> bufferIndex = Maps.<Integer, StringBuffer>newHashMap();

    akorát že typové parametry doplní překladač sám. U konstruktorů to bohužel takhle nefunguje (v Javě 7 na to nejspíš bude nějaká obezlička, kterou navrhuje Neal Gafter).

  8. alfonz:

    ok, pak se omlouvám, mám zjevně solidní mezery ve vzdělání. Má někdo nějakou doporučenou literaturu ohledně typové inference?

  9. Novoj:

    K typové inferenci jsem našel např. (pro mě je to také nový termín ;-) ):
    http://en.wikipedia.org/wiki/Type_inference

  10. Ladislav Thon:

    Česky by se řeklo odvozování typů, takže spíš inference typů, ale víme, jak je to s češtinou u lidí z IT :-) Jinak ohledně odvozování typových parametrů konkrétně v Javě 5 a výš se dá něco dočíst třeba ve starém dobrém Generics FAQ: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#Type%20Argument%20Inference

Nechte zde svůj komentář

Opište prosím text z obrázku: