Rudolf Pecinovský rudolf@pecinovsky.cz Kontejnery Rudolf Pecinovský rudolf@pecinovsky.cz
Obsah s odkazy Dědění rozhraní Úvod do typových parametrů Třída Object a její metody Kontejnery – kolekce, množiny, seznamy Návrhový vzor Iterátor Pole Rozhraní Comparable a Comparator
Balíčky Problémy velkých aplikací Balíčky v jazyku Java Příkaz package Kořenový balíček Plný název třídy a příkaz import Balíčky a BlueJ Implicitní modifikátor přístupu Pravidla pro tvorbu balíčků Statický import 25–26 310–334
Problémy velkých aplikací Rozsáhlé aplikace používají velké množství názvů objektů a jejich zpráv, které různé části programu sdílí V rozsáhlých programátorských týmech se těžko zaručovalo, že někdo z jedné skupiny nezavede objekt se stejným názvem jako někdo z jiné skupiny Pro řešení tohoto problému se v 70. letech začaly používat jmenné prostory V rámci jmenného prostoru je možné používat názvy svobodně, názvy z jiného jmenného prostoru je třeba kvalifikovat názvem jejich jmenného prostoru Jmenné prostory mají hierarchickou stromovou strukturu obdobnou struktuře složek (adresářů) na disku Copyright © 2006, Rudolf Pecinovský VŠE – 05
Balíčky v jazyku Java Java zavádí ekvivalent jmenných prostorů pod názvem package – balíček, balík Balíček může obsahovat podbalíčky Balíček obsahující podbalíčky označujeme jako rodičovský Podbalíčky označujeme jako dceřiné balíčky či potomky svého rodiče Název balíčku se skládá z názvu rodičovského balíčku následovaného tečkou a vlastním názvem daného balíčku Příklady: java.lang.reflect, java.util.concurrent Balíčky standardní knihovny a podbalíčky balíčku java Copyright © 2006, Rudolf Pecinovský VŠE – 05
Balíčky a uložení na disku Při uložení přeložených souborů na disku musí umístění souborů ve složkách odpovídat jejich umístění v balíčku => Každému balíčku je přiřazena jeho složka Složka se musí jmenovat přesně stejně jako daný balíček (včetně velikosti písmen) Podbalíčku daného balíčku je přiřazena podsložka rodičovského balíčku Třída může patřit pouze do jediného balíčku (Pra)rodičovský balíček celého stromu nemá jméno a označujeme jako kořenový balíček Rozbalené balíčky java.lang a java.util Copyright © 2006, Rudolf Pecinovský VŠE – 05
Balíčky v jazyku Java Aplikace může mít kořenový balíček umístěný v několika složkách; obsah všech těchto složek je pak považován za obsah kořenového balíčku aplikace Obdobně se slučují obsahy párových složek v různých stromech Seznam složek s kořenovými balíčky je třeba předat překladači a virtuálnímu stroji v proměnné CLASSPATH Vývojová prostředí od této povinnosti osvobozují, informaci vygenerují a dodají sama Na obrázku vpravo je kořenovou složkou standardní knihovny složka C:\Java\JDK.1.5.0\src Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příkaz package Zdrojový kód každé třídy musí začínat příkazem package deklarujícím název balíčku, do nějž třída patří; před ním smí být jen bílé znaky a komentáře Příkaz_package: package název_balíčku ; Výjimkou jsou pouze třídy z kořenového balíčku, které příkazem package naopak začínat nesmí Kořenový balíček není plnohodnotný, řada konstrukcí v něm není použitelná ® používá se proto jen pro rychlé testy proveditelnosti některých nápadů (a v prvních lekcích výuky) Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příkaz import 1/2 Název třídy sestává z názvu jejího balíčku následovaného tečkou a vlastním názvem třídy Příklad: java.lang.String (třída String je v balíčku lang, který je podbalíčkem balíčku java, jenž je podbalíčkem kořenového balíčku Uvnitř balíčku se na třídu lze odvolávat jejím vlastním názvem bez kvalifikace názvem jejího balíčku Bez kvalifikace se lze odvolávat i na třídy z balíčku java.lang Při posílání zprávy třídě z jiného balíčku je třeba uvádět její plný název včetně kvalifikace balíčkem (java.util.List) Z předchozího pravidla se lze „vylhat“ uvedením názvu třídy v příkazu import Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příkaz import 2/2 Příkaz_import: import název_balíčku . vlastní_název_třídy ; import název_balíčku . * ; Druhá verze příkazu import importuje všechny třídy z daného balíčku Každý příkaz import může importovat pouze jedinou třídu nebo všechny třídy z jediného balíčku Příkaz import musí být uveden na počátku zdrojového kódu, před ním smí být pouze příkaz package nebo jiný import Příklad: import java.io.File; //Třída File z balíčku java.io import java.util.*; //Všechny třídy z balíčku java.util Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příklad použití package a import package adventura.rup; import java.util.Arrays; import adventura.rámec.ABatoh; import adventura.rámec.AHra; import adventura.rámec.AMístnost; import adventura.rámec.KrokTestu; import adventura.rámec.TestHry; /***************************************************** * Instance třídy {@code Hra_RP} představují hru, * při níž se hráč ocitne v malém bytě, který je třeba * projít a najít v něm ledničku. */ public class Hra_RP extends AHra { //== KONSTANTNÍ ATRIBUTY TŘÍDY ======================= Copyright © 2006, Rudolf Pecinovský VŠE – 05
Balíčky a BlueJ 1/2 BlueJ považuje každý balíček za samostatný projekt; pro každý balíček tedy otevře samostatné aplikační okno Mezi balíčky/projekty není možné natahovat čáry závislostí ani dědičnosti – příslušné informace je třeba zadat „ručně“ přímo do zdrojového kódu Každý balíček/projekt musí mít ve své složce svůj vlastní soubor package.bluej a/nebo bluej.pkg BlueJ původně používal soubor bluej.pkg, ale ten nebylo možno asociovat s aplikací, protože přípona byla již blokována jiným programem. Proto autoři zvolili jinou příponu a soubor s informacemi o vzhledu aplikačního okna a diagramu tříd se nyní jmenuje package.bluej. V zájmu zpětné kompatibility však BlueJ reaguje i na starší soubor. Kořenovým balíčkem/projektem stromu projektů je projekt, v jehož rodičovské složce se již soubor package.bluej ani bluej.pkg nenachází Copyright © 2006, Rudolf Pecinovský VŠE – 05
Balíčky a BlueJ 2/2 Balíčky mají v diagramu tříd vlastní ikony připomínající ikony složek Balíček lze vytvořit zadáním příkazu z místní nabídky plochy Balíček lze odstranit zadáním příkazu z místní nabídky balíčku S ikonou rodičovského balíčku není možno nijak hýbat V místní nabídce rodičovského balíčku je seznam názvů rodičovských balíčků až ke kořeni; klepnutím na název se přesuneme do projektu příslušného danému balíčku Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příklad Copyright © 2006, Rudolf Pecinovský VŠE – 05
Implicitní modifikátor přístupu Vedle modifikátorů přístupu public a private existuje i implicitní modifikátor přístupu, který se uplatní, když žádný modifikátor přístupu neuvedeme Entity s implicitním modifikátorem přístupu jsou viditelné a dosažitelné pouze v rámci daného balíčku Implicitní modifikátor přístupu bývá označován jako package private; je to ale pouze označení pro doprovodné texty, v programu se nijak neoznačuje Zdrojové kódy tříd s implicitním přístupem nemusejí být v souborech, které mají stejné jméno jako třída => těchto tříd může být i několik v jednom souboru Jako neveřejné je vhodné deklarovat opravdu jen třídy určené pro interní potřebu celého daného balíčku Copyright © 2006, Rudolf Pecinovský VŠE – 05
Pravidla pro tvorbu balíčků Balíček by měl být „jednoúčelový“, tj. obsahovat pouze třídy a rozhraní přímo se vážící k řešení stejného problému Název balíčku má být dle konvencí jen malými písmeny U prodávaných aplikací by měl začátek názvu balíčku odpovídat internetové adrese výrobce Kdyby katedra informačních technologií dodávala aplikace, měly by být v podbalíčcích balíčku cz.vse.kit Tato konvence zabezpečí jedinečnost názvu použitých tříd na celém světě (za podmínky, že výrobce zaručí jedinečnost názvu v rámci svého webu) Copyright © 2006, Rudolf Pecinovský VŠE – 05
Statický import Java 5.0 zavedla možnost importovat i statické členy jiných tříd a používat je pak bez kvalifikace Statický_import: import static balíček . třída . člen ; import static balíček . třída . * ; Používání statického importu znepřehledňuje program Má smysl pouze u členů, které se používají velice často a u nichž nehrozí vyvolání dojmu, že se jedná o členy dané třídy Copyright © 2006, Rudolf Pecinovský VŠE – 05
Úvod do typových parametrů Knihu je možno stáhnout ve formátu PDF na adrese http://knihy.pecinovsky.cz/java5novinky
Učebnice ke kurzu Vydal Computer Press 2005, ISBN 80-251-0615-2 Podrobně vysvětluje řadu konstrukcí, které jinde podrobně česky vysvětlené nenajdete: Parametrizované typy a typové parametry Výčtové typy Anotace Kódování znaků rozšířené sady Unicode Je vyprodaná, ale můžete si ji legálně zdarma stáhnout ve formátu PDF na adrese http://knihy.pecinovsky.cz/java5novinky Copyright © 2006, Rudolf Pecinovský VŠE – 05
Řešený problém Kontejner = objekt určený k ukládání jiných objektů Do verze 5.0 nebylo v Javě možno definovat obecný kontejner (datový typ) tak, abychom typ skutečně ukládaných hodnot mohli zadat až při vytváření jeho konkrétní instance Kontejnery ze standardní knihovny byly ochotny ukládat objekty libovolného objektového typu; typovou kontrolu bylo možno provést až za běhu Autoři Javy se inspirovali řešením v jazyku C++ a ve verzi 5.0 zavedli do jazyka typové parametry a parametrizované typy Parametrizované typy jsou často označovány jako generické typy angl. generic = 1. obecný, obecně použitelný, 2. rodový, 3. neznačkový (např. lék) umožňující generovat celou řadu Copyright © 2006, Rudolf Pecinovský VŠE – 05
Parametrizované typy a metody Své parametry mají nejenom metody, ale i typy (rozhraní a třídy) – hovoříme o parametrizovaných typech Parametry třídy uvádějí typy použitých objektů – označujeme proto jako typové parametry Typové parametry uvádíme ve špičatých závorkách za názvem třídy – např. List<String> Jako typové parametry je možno použít pouze objektové typy Používané zkratky: PT – parametrizovaný typ / parametrizované typy PM – parametrizovaná metoda / parametrizované metody PTM – parametrizované typy a metody TP – typový parametr Parametrizovaná (generická) metoda != metoda s parametry Copyright © 2006, Rudolf Pecinovský VŠE – 05
Typově parametry a překlad Typové parametry slouží jako informace o typech objektů pouze pro překladač Rozhoduje o přípustnosti použitého typu V případě potřeby vloží potřebné přetypování Přeložený kód je od těchto informací „očištěn“ (erasure) a žádné dodatečné typové informace již neobsahuje – překladač upozorňuje na možné kolize v přeloženém kódu Ve zdrojovém kódu můžeme použít parametrizovaný typ i bez typových parametrů, překladač ale ohlásí varování Copyright © 2006, Rudolf Pecinovský VŠE – 05
Definice vlastního parametrizovaného typu Třída FrontaP pracuje s objekty typu E FrontaP = Fronta s typovým Parametrem Pomocí typového parametru definujeme skutečný typ objektů ve frontě FrontaP<String> FrontaP<Elipsa> FrontaP<Účet> Hodnota TP specifikuje typ parametru či typ návratové hodnoty public class FrontaP<E> { List<E> prvky = new ArrayList<E>(); public FrontaP() {} public void zařaď( E prvek ) { prvky.add( prvek ); } public boolean isPrázdná() { return (prvky.size() == 0); public E další() { E ret = prvky.get(0); prvky.remove(0); return ret; public String toString() { return prvky.toString(); Copyright © 2006, Rudolf Pecinovský VŠE – 05
Omezení hodnot typových parametrů Občas potřebujeme, aby typové parametry vyhovovaly daným omezením – např. aby implementovaly nějaké rozhraní public class IntervalU<T extends Comparable<T>> { private final T dolní, horní; public IntervalU( T dolní, T horní ) { if( dolní.compareTo( horní ) > 0 ) throw new IllegalArgumentException( "Dolní mez nemůže být větší než horní" ); this.dolní = dolní; this.horní = horní; } public T getDolní() { return dolní; } public T getHorní() { return horní; } public boolean uvnitř( T t ) { return (dolní.compareTo( t ) <= 0) && (t.compareTo( horní ) <= 0); I pro implementaci rozhraní se používá extends Copyright © 2006, Rudolf Pecinovský VŠE – 05
Žolíky Slouží k řešení problémů způsobených omezeními dědičnosti parametrizovaných typů Může-li na daném místě být objekt libovolného typu, použijeme žolík ? Může-li být na daném místě objekt libovolného typu vyhovujícího zadanému omezení, použijeme žolík s příslušným omezením Jakákoliv třída: <?> Třída implementující rozhraní X, resp. potomek X: <? extends X> Omezení lze kombinovat: <? extends Comparable<? extends T>> Copyright © 2006, Rudolf Pecinovský VŠE – 05
Kontejnery Obecně o kontejnerech Architektura knihovny kolekcí Rozhraní Collection<E>
Obecně o kontejnerech Kontejnery = objekty sloužící k uložení jiných objektů Kontejnery dělíme na Statické – „narodí se“ s kapacitou, která jim zůstane až do smrti Přepravka Pole Dynamické – mohou se přizpůsobovat okamžitým potřebám Množina Seznam Fronta Strom Mapa (slovník) … I dynamické kontejnery mohou definovat „konstanty“, které pak již počet ani hodnoty uchovávaných prvků nemění Copyright © 2006, Rudolf Pecinovský VŠE – 05
Knihovna kolekcí v Javě 6 Copyright © 2006, Rudolf Pecinovský VŠE – 05
Knihovna kolekcí – architektura Copyright © 2006, Rudolf Pecinovský VŠE – 05
Zjednodušená knihovna kolekcí Copyright © 2006, Rudolf Pecinovský VŠE – 05
Kolekce, množiny, seznamy
Rozhraní Collection<E> – kolekce 1/2 boolean add( E o ) Přidá zadaný prvek, vrátí informaci, zda se kolekce změnila boolean addAll( Collection<? extends E> c ) Přidej všechny prvky ze zadané kolekce, vrátí zda se kolekce změnila void clear() Vyprázdni kolekci boolean contains( Object o ) Zjistí, zda kolekce obsahuje zadaný prvek boolean containsAll( Collection<?> c ) Zjistí, zda kolekce obsahuje všechny prvky ze zadané kolekce boolean isEmpty() Zjistí, zda je kolekce prázdná Copyright © 2006, Rudolf Pecinovský VŠE – 05
Rozhraní Collection<E> – kolekce 2/2 boolean remove( Object o ) Odstraní zadaný objekt z kolekce a vrátí informaci, zda se kolekce změnila boolean removeAll( Collection<?> c ) Odstraní z kolekce všechny prvky ze zadané kolekce; vrátí, zda se tím změnila int size() Vrátí počet prvků v kolekci Object[] toArray() Vrátí pole s prvky kolekce Object[] toArray( Object[] a ) Vrátí pole prvků zadaného typu s prvky kolekce; vejdou-li se do pole v parametru, vrátí toto pole, nevejdou-li se, vytvoří nové Potomek rozhraní Iterable => Iterator<E> iterator() Vrátí iterátor zpřístupňující prvky uložené v kolekci Copyright © 2006, Rudolf Pecinovský VŠE – 05
Rozhraní Set<E> – množina Množiny jsou zvláštním druhem kolekcí, v nichž může být každý prvek uložen pouze jednou Přebírá všechny metody svého rodiče, nic vlastního nepřidává Upřesňuje pouze kontrakt: v množině se nesmějí vyskytovat dva stejné prvky, tj. prvky, pro něž platí e1.equals(e2) == true Množiny se (obecně) nezajímají o pořadí uložených prvků, a proto o něm ani neinformují Implementace jsou optimalizovány na rychlost vyhledávání uložených instancí Konkrétní implementace budou ukázány po probrání iterátorů Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příklad: Molekuly public void animace { Set<Molekula> vyhodit = new HashSet<Molekula>(); for(;;) { for (Molekula m1 : molekuly) { if (vývěva.vcucne(m1)) { vyhodit.add(m1); //Nemohu vyhodit hned break; } for (Molekula m2 : molekuly) { if ((m1 != m2) && m1.kolize(m2)) odraž(m1, m2); } //for m2 odrazOdStěn(); } //for m1 for (Molekula m : vyhodit) { molekuly.remove(m); //Dodatečné vyhození porodnice.zkusNovou(); } //for(;;) } //animace Copyright © 2006, Rudolf Pecinovský VŠE – 05
Rozhraní List<E> – seznam 1/2 Oproti množině uchovává pořadí uložených prvků Díky pořadí umožňuje uložit několikrát týž objekt Oproti Collection přidává metody reagující na umístění (= indexy) uložených/ukládaných prvků Počáteční prvek v seznamu má index 0 Poslední prvek má index size()-1 void add(int index, E element) Přidá zadaný prvek na zadanou pozici, pozice následujících se zvětší boolean addAll(int index, Collection<? extends E> c) Přidá prvky zadané kolekce na zadanou pozice a pozice následující E remove(int index) Odstraní instanci na zadané pozici Copyright © 2006, Rudolf Pecinovský VŠE – 05
Rozhraní List<E> – seznam 2/2 E get(int index) Vrátí instanci uloženou na zadané pozici int indexOf(Object o) Vrátí pozici prvního výskytu zadané instance int lastIndexOf(Object o) Vrátí pozici posledního výskytu zadané instance E set(int index, E element) Nahradí prvek na zadané pozici zadaným prvkem List<E> subList(int fromIndex, int toIndex) Vrátí seznam obsahující prvky na pozicích od fromIndex do toIndex-1 včetně Copyright © 2006, Rudolf Pecinovský VŠE – 05
Implementace seznamu Třída ArrayList Třída LinkedList Závěr Úspornější na paměť Běžné přidání prvku je rychlejší Umí rychle přistupovat ke kterémukoliv prvku Umí rychle přidávat a odebírat prvky pouze na konci seznamu Třída LinkedList Náročnější na paměť (prvek si pamatuje odkazy na předchůdce a následníka) Běžné přidání je pomalejší (musí se vytvořit odkazy) Umí rychle přistupovat pouze k prvkům na začátku a konci Umí rychle přidávat a odebírat prvky kdekoliv v seznamu Závěr Většinou je výhodnější ArrayList Je-li prvků hodně (> 16) a potřebujeme-li často přidávat či odstraňovat prvky z prostředku seznamu a málo náhodně přistupovat, je lepší LinkedList Copyright © 2006, Rudolf Pecinovský VŠE – 05
Návrhový vzor Iterátor Problémy zapouzdření kontejnerů Návrhový vzor Iterátor Interface Iterator Použití iterátoru ve standardní knihovně Porovnání cyklu s iterátorem a bez něj Iterovatelné třídy 532–538
Problémy zapouzdření kontejnerů Instance, které ukládáme do kontejneru, neodkládáme jako do popelnice – budeme je chtít v budoucnu použít Kontejner nás však nemůže nechat se ve svých útrobách přehrabovat – to by nám musel prozradit svou implementaci Potřebujeme mít možnost se kdykoliv dostat k uloženým datům, aniž by kontejner byl nucen cokoliv prozradit o své implementaci Problém řeší aplikace návrhového vzoru Iterátor Copyright © 2006, Rudolf Pecinovský VŠE – 05
Návrhový vzor Iterátor – princip Kontejner definuje speciální třídu – iterátor, jejíž instance ví, jak jsou svěřená data uložena Tomu, kdo chce pracovat s uloženými daty vrátí na požádání instanci iterátoru, který mu přístup k uloženým datům zprostředkuje Instance iterátoru na požádání vrátí odkaz na další z instancí uložených v kontejneru Až iterátor všechna data vyčerpá, oznámí, že už další nejsou Tazatel se tak dostane ke všem uloženým datům, aniž by se dozvěděl, jak jsou vlastně uložena Copyright © 2006, Rudolf Pecinovský VŠE – 05
interface Iterator package java.util; public interface Iterator<E> { /** Je ještě nějaká instance k dispozici? */ boolean hasNext(); /** Vrátí odkaz na další instanci. */ E next(); /** Odebere z kolekce * naposledy vrácenou instanci. * Metoda nemusí být plně implementována. */ void remove(); } Copyright © 2006, Rudolf Pecinovský VŠE – 05
Ukázka použití public static void println( String název, Collection<?> kolekce ) { System.out.print( název + ": (" ); String oddělovač = ""; for( Iterator<?> it = kolekce.iterator(); it.hasNext(); /* nic */ ) Object o = it.next(); String s = o.toString(); System.out.print( oddělovač + s ); oddělovač = ", "; } System.out.println( ")" ); Copyright © 2006, Rudolf Pecinovský VŠE – 05
Totéž s novou verzí cyklu public static void println( String název, Collection<?> kolekce ) { System.out.print( název + ": (" ); String oddělovač = ""; for( Object o : kolekce ) //Objekt již mám přiřazen String s = o.toString(); System.out.print( oddělovač + s ); oddělovač = ", "; } System.out.println( ")" ); Copyright © 2006, Rudolf Pecinovský VŠE – 05
Použití ve třídě AbstractCollection public abstract Iterator<E> iterator(); public boolean contains(Object o) { Iterator<E> ie = iterator(); if (o==null) { while (ie.hasNext()) if (ie.next()==null) return true; } else { if (o.equals(ie.next())) } return false; Copyright © 2006, Rudolf Pecinovský VŠE – 05
Totéž s novou verzí cyklu public abstract Iterator<E> iterator(); public boolean contains(Object o) { if (o==null) { for(E e : this ) if (e == null) return true; } else { for( E e : this ) if (o.equals(e)) } return false; Copyright © 2006, Rudolf Pecinovský VŠE – 05
Porovnání cyklu s iterátorem a bez něj //Zjednodušený for = „for each“ for( E e : kont ) { //Tělo cyklu pracující s prvkem e } //Klasická verze cyklu for( Iterator<E> ie = kont.iterator; ie.hasNext(); ) { E e = ie.next(); //Tělo cyklu pracující s prvkem e Zjednodušenou verzi nelze použít, pokud chceme v těle pracovat s kontejnerem (např. z něj prvek vyhodit či přidat) Copyright © 2006, Rudolf Pecinovský VŠE – 05
Iterovatelné třídy Zjednodušenou verzi cyklu můžeme použít v případě, že třída sloužící jako zdroj dat je iterovatelná, tj. implementuje rozhraní Iterable<E> Rozhraní požaduje implementaci jediné metody: public Iterator<E> iterator() Metoda bývá většinou implementována tak, že vrací instanci vnořené nebo vnitřní třídy Copyright © 2006, Rudolf Pecinovský VŠE – 05
Implementace množiny a hešové tabulky Jsou postaveny na hešových tabulkách HashSet Nepatrně rychlejší ukládání nového prvku do množiny Maličko pomalejší procházení množiny iterátorem Nezaručuje pořadí, v němž bude iterátor poskytovat uložené prvky LinkedHashSet Nepatrně pomalejší ukládání nového prvku do množiny Maličko rychlejší procházení množiny iterátorem Iterátor bude vracet prvky ve stejném pořadí, v jakém byly do množiny ukládány Pokud prvek odstraním a zase vložím, bude do vložení dalšího prvku považován za naposledy vložený Copyright © 2006, Rudolf Pecinovský VŠE – 05
Množiny a mapy Množina Mapa Rozhraní java.util.Map<K,V> Implementace množin a map Metody hashCode() a equals(Object) Implementace hashCode() Příklad mapy: příkazy pro CPU 36–41 45–49 82–96 522–531
Mapa Mapa je kontejner, který spolu s ukládanou informací ukládá i tzv. klíč, podle kterého se bude daná informace hledat Ukládám-li informace o osobách, budu je vyhledávat buď podle příjmení a jména, nebo ještě lépe podle rodného čísla Klíče se uvnitř ukládají do množiny a každý klíč zná odkaz na s ním spřaženou hodnotu Ukládání do množiny vynucuje jedinečnost klíče, tj. nesmí se vyskytnout dva stejné klíče pro různé hodnoty Některé operace: Uložit dvojici <Klíč;Hodnota> Získat uloženou hodnotu zadám-li klíč Získat množinu klíčů uložených hodnot Získat kolekci uložených hodnot (stejná hodnota může být uložena se dvěma klíčí – pak bude v mapě dvakrát => hodnoty nemohou být v množině) Copyright © 2006, Rudolf Pecinovský VŠE – 05
Rozhraní java.util.Map<K,V> 1/2 K = key – třída klíče V = value – třída hodnoty static interface Map.Entry<K,V> Rozhraní definované jako interní datový typ v rozhraní Map<K,V> void clear() Vyprázdní danou mapu boolean containsKey(Object key) boolean containsValue(Object value) Set<Map.Entry<K,V>> entrySet() Mapa je vlastně množina přepravek <Klíč;Hodnota>, i když tak není definovaná Collection<V> values() Vrátí kolekci hodnot v mapě (jedna hodnotu v ní může být vícekrát). V get(Object key) Vrátí hodnotu přiřazenou danému klíči Copyright © 2006, Rudolf Pecinovský VŠE – 05
Rozhraní java.util.Map<K,V> 2/2 boolean isEmpty() Set<K> keySet() Vrátí množinu všech použitých klíčů V remove(Object key) Vyjme klíč s přidruženou hodnotou z mapy a vrátí hodnotu V put(K key, V value) Přiřadí zadanému klíči zadanou hodnotu, měl-li již předtím klíč nějakou hodnotu přiřazenou, vrátí ji jako návratovou hodnotu. void putAll(Map<? extends K,? extends V> t) int size() Copyright © 2006, Rudolf Pecinovský VŠE – 05
Implementace množin a map Množiny a mapy se většinou implementují prostřednictvím hešových tabulek Do hešové tabulky se hodnota ukládá na pozici odvozenou z hodnoty heš-kódu dané instance Jsou-li hodnoty v tabulce rovnoměrně rozprostřeny, je jejich vyhledávání nesmírně rychlé Je-li heš-kód vypočítáván nešikovně, kumulují se hodnoty v tabulce na jednom místě a při hledání hodnoty se musí procházet seznam všech hodnot, které patří v tabulce na danou pozici Abychom byla práce s hešovými tabulkami do nejefektivnější, musíme umět správně spočítat heš-kód Copyright © 2006, Rudolf Pecinovský VŠE – 05
Metody hashCode() a equals(Object) Metoda hashCode() přiřazuje instanci celé číslo – její heš-kód Platí kontrakt: a.equals(b) => a.hashCode() == a.hashCode() Další požadavky Hodnota musí být co nejrovnoměrněji rozprostřena Hodnota musí být spočtena dostatečně rychle Předchozí požadavky si trochu odporují, proto si řada instancí jednou spočtenou hodnotu pamatuje, aby ji příště nepočítala Doporučení, jak počítat heš-kód: J. Bloch: Effective Java, Addison-Wesley, ISBN 0-201-31005-8 J. Bloch: Java efektivně, Grada 2002, ISBN 80-247-0416-1 Copyright © 2006, Rudolf Pecinovský VŠE – 05
Implementace hashCode() U referenčních objektových typů můžeme akceptovat implementaci obdrženou od systému U hodnotových objektových typů, tj. u těch, pro něž jsme definovali metodu equals(Object), musíme k této metodě definovat i vlastní verzi metody hashCode() Metodu musíme naprogramovat tak, aby byl dodržen kontrakt a aby se navíc hodnoty co nejlépe rozprostřely Možná podoba definice metody hashCode()pro třídu Pozice public int hashCode() { if( hashCode == 0 ) hashCode = 37*(135749 + x) + y; return hashCode; } Deklarace: private int hashCode; Copyright © 2006, Rudolf Pecinovský VŠE – 05
Pole Základní charakteristika Pole jako objekt Výhody a nevýhody Inicializace polí Vícerozměrná pole Proměnný počet parametrů 97–102 563–586
Základní charakteristika Pole je typ kontejneru, který je implementován přímo ve strojovém kódu většiny procesorů a současně v syntaxi prakticky všech jazyků Ostatní kontejnery jsou proto velmi často implementovány jako pole Každý prvek v poli má svoji přesnou adresu – index Indexy v polích jsou stejné jako v seznamech Index „prvního“ prvku je 0 (tj. jedná se o „nultý“ prvek) Index posledního prvku = počet prvků – 1 Copyright © 2006, Rudolf Pecinovský VŠE – 05
Pole jako objekt V Javě je pole standardní objekt se všemi vlastnostmi objektu Vytváří se pomocí operátoru new Můžete jej použít všude, kde lze použít objekt Má definovány všechny metody, které objekty získávají od systému Má veřejný konstantní atribut length, který vrací počet jeho prvků Na rozdíl od metody size() dříve probraných kontejnerů nevrací počet prvků, které jsme do pole uložili, ale kolik prvků se do pole vejde Copyright © 2006, Rudolf Pecinovský VŠE – 05
Výhody a nevýhody Výhody Nevýhody + / – Maximálně efektivní (má přímou podporu v jazyku i procesoru) Je možné je jednoduše inicializovat Umožňuje ukládat i hodnoty primitivních typů Lze je použít všude tam, kde je možno/nutno použít objekt Pro pole, ze kterého jen čtu, lze použít příkaz for(:) Nevýhody Má pevnou velikost zadávanou při jeho vytváření Nelze poznat, které prvky jsme do něj již vložili Na rozdíl od seznamu nepodporuje vkládání prvku mezi existující Na rozdíl od mapy používá jako klíč vždy číselný index + / – Index prvků je neměnný Copyright © 2006, Rudolf Pecinovský VŠE – 05
Deklarace a inicializace 1/2 Deklarace_pole: [ Modifikátor… ] Typ [] Název [ = Inicializace_pole ] ; Inicializace_pole: { Výraz [ , Výraz ] [ , ] } Vytvoření_pole: new Typ [] [ Inicializace_pole ] public static int součet( int[] ii ) { int součet = 0; for( int i : ii ) součet += i; return součet; } public static void test() { int is = součet( new int[] { 1,2,3,4,5 } ); int[] ii = {9, 8, 7, 6}; int is = součet( ii ); Cyklus „for each“ Vytvoření a inicializace pole „na poslední chvíli“ Copyright © 2006, Rudolf Pecinovský VŠE – 05
Deklarace a inicializace 2/2 Vytvořené pole je (na rozdíl od některých jiných jazyků) inicializované nulami / prázdnými odkazy / … Chci-li do pole ukládat, musím použít klasickou podobu cyklu Metoda může vracet pole stejně jako jiný objekt public static String[] číslaSlovy( int počet ) { String[] ret = new String[počet]; for( int i=0; i < ret.length; i++ ) ret[i] = Slovy.číslo( i ); } return ret; Copyright © 2006, Rudolf Pecinovský VŠE – 05
Vícerozměrná pole Typ prvků pole může být cokoliv, tedy i jiné pole Pole, jehož prvky jsou jiná pole, označujeme jako vícerozměrné pole Počet rozměrů (tj. hloubka vnoření) není omezen (leda pamětí) Vícerozměrné pole nemusí být obdélníkové, každé z polí, které je prvkem pole vyššího řádu, může mít svůj rozměr Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příklad s vícerozměrným polem public static void vícerozměrné() { int[][] ip2 = { { 1, 2, 3, 4, 5 }, { 10, 20, 30, 40 } }; for( int[] ip1 : ip2 ) { for( int i : ip1 ) System.out.print( i + " - " ); System.out.println(); } for( int i1=0; i1 < ip2.length; i1++ ) for( int i2=0; i2 < ip2[i1].length; i2++ ) System.out.print( ip2[i1][i2] + " - " ); Inicializace vícerozměrného pole dvěma jednorozměrnými poli Parametrem vnějšího cyklu je pole Parametrem vnitřního cyklu je celočíselná proměnná Parametrem obou cyklů jsou celočíselné proměnné – indexy prvků pole Na prvek pole se odkazujeme prostřednictvím jeho indexů Výsledek činnosti metody Copyright © 2006, Rudolf Pecinovský VŠE – 05
Proměnný počet parametrů Java 5.0 umožnila definovat metody, které nemají předem daný počet parametrů Všechny parametry, o nichž předem nevíme, kolik jich bude, musejí být stejného typu V deklaraci metody označujeme proměnný (=předem neznámý) počet parametrů trojtečkou za názvem jejich typu Deklarace proměnného počtu parametrů musí být poslední deklarací v seznamu parametrů (=> smí být jen jedna) Metoda přebírá parametry, jejichž počet nezná, v poli Copyright © 2006, Rudolf Pecinovský VŠE – 05
Příklad na proměnný počet parametrů public static int max( int... ii ) { if( (ii==null) || (ii.length==0) ) return CHYBA(); int max = ii[0]; for( int i=1; i < ii.length; i++ ) { if( ii[i] > max ) max = ii[i]; } return max; public static void testMax() { int m = max( 1, 6, 4, 5 ); Parametr ii je chápán jako pole Copyright © 2006, Rudolf Pecinovský VŠE – 05
Děkuji za pozornost Rudolf Pecinovský http://vyuka.pecinovsky.cz/vse mail: rudolf@pecinovsky.cz ICQ: 158 156 600