7. Dynamické datové struktury



Podobné dokumenty
Seznamy a iterátory. Kolekce obecně. Rozhraní kolekce. Procházení kolekcí

boolean hasnext() Object next() void remove() Kolekce

typová konverze typová inference

Výčtový typ strana 67

Datové struktury strana 81

1. Téma 12 - Textové soubory a výjimky

Regulární výrazy. Vzory

Soubor jako posloupnost bytů

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

9. přednáška - třídy, objekty

Seminář Java IV p.1/38

Úvod do programovacích jazyků (Java)

InputStream. FilterInputStream

Typický prvek kolekce pro české řazení

Datové struktury. alg12 1

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Generické programování

Kolekce, cyklus foreach

Abstraktní datové typy: zásobník

Výčtové typy a kolekce v Javě, generické typy

Výčtové typy Kolekce a JFC Iterátory Přehled JFC Generické typy

Výčtové typy Kolekce a JFC Iterátory Přehled JFC Generické typy Příklad - Spojový seznam

Vytváření a použití knihoven tříd

Výčtové typy a kolekce v Javě, generické typy

Kolekce ArrayList. Deklarace proměnných. Import. Vytvoření prázdné kolekce. napsal Pajclín

7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd

Pole ( Array ) p2[1] = new Point(5, 6); // assignment PJV04 1

PŘETĚŽOVÁNÍ OPERÁTORŮ

8. přednáška: Soubory a proudy

Abstraktní třída a rozhraní

Seminář Java V p.1/49

Algoritmizace a programování

20. Projekt Domácí mediotéka

Stromy. Příklady. Rekurzivní datové struktury. Základní pojmy

Příklad : String txt1 = new String( Ahoj vsichni! ); //vytvoří instanci třídy String a přiřadí ji vnitřní hodnotu Ahoj vsichni!

Java - řazení objektů

Datové typy v Javě. Tomáš Pitner, upravil Marek Šabo

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

6. Problém typové anonymity prvků v kolekci Sjednocení typově rozdílných prvků pomocí rozhraní Kolekce pro jeden typ prvků...

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Programování v C++, 2. cvičení

Pole a kolekce. v C#, Javě a C++

součet cvičení celkem. známka. Úloha č.: max. bodů: skut. bodů:

Iterator & for cyklus

Algoritmizace a programování

Množina v C++ (set, multiset).

Java - výjimky. private void vstup() throws IOException {... }

Generické typy. Podrobněji: The Java Language Specification ( Third Edition ) , 18

Práce s textem. Třída Character. Třída Character. Třída Character. reprezentuje objekty zapouzdřující hodnotu typu char (boxing / unboxing)

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Programovací jazyk Java

Implementace abstraktních datových typů. Zásobník (Stack) Fronta (Queue) Obousměrná fronta (Dequeue) Vektor (Vector) Seznam (List)

Třída jako zdroj funkcí

Úvod do programovacích jazyků (Java)

Parametrizované třídy Generics generické třídy. JDK zavádí mimo jiné tzv. parametrizované třídy - generics

11. Dědičnost. Dědičnost strana 103

Pokud zadání nerozumíte nebo se vám zdá nejednoznačné, zeptejte se. Pište čitelně, nečitelná řešení nebudeme uznávat.

Programování v C++ 2, 8. cvičení

Statické proměnné a metody. Tomáš Pitner, upravil Marek Šabo

Programování v Javě I. Leden 2008

Z. Kotala, P. Toman: Java ( Obsah )

Datové struktury. Obsah přednášky: Definice pojmů. Abstraktní datové typy a jejich implementace. Algoritmizace (Y36ALG), Šumperk - 12.

Java a XML. 10/26/09 1/7 Java a XML

Programování v Javě I. Únor 2009

Projekty pro výuku programování v jazyce Java

Úvod do programování - Java. Cvičení č.4

Programování v jazyku Java pole, kolekce, typované kolekce

Obsah přednášky. Výjimky, výčtové typy a kolekce v Javě. Nestandardní situace. Výjimky (Exceptions)

Výjimky, výčtové typy a kolekce v Javě

Paměť počítače. alg2 1

Jazyk C# (seminář 6)

JAVA. java.lang.system

9. Polymorfismus a rozhraní

15. Projekt Kalkulačka

Programové konvence, dokumentace a ladění. Programování II 2. přednáška Alena Buchalcevová

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Skripta ke školení. Základy VBA. vypracoval: Tomáš Herout. tel:

Textové soubory. alg9 1

int t1, t2, t3, t4, t5, t6, t7, prumer; t1=sys.readint();... t7=sys.readint(); prume pru r = r = ( 1+t 1+t t3+ t3+ t4 t5+ t5+ +t7 +t7 )/ ;

Správa paměti. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta, 2016

Kolekce. BD6B36PJV 5 Fakulta elektrotechnická České vysoké učení technické

KTE / ZPE Informační technologie

7. Datové typy v Javě

17. Projekt Trojúhelníky

OOPR_05. Případové studie

Java Výjimky Java, zimní semestr

Abstraktní datové typy

Teoretické minimum z PJV

Projekt Škola strana 179

Základní pojmy. Úvod do programování. Základní pojmy. Zápis algoritmu. Výraz. Základní pojmy

8 Třídy, objekty, metody, předávání argumentů metod

UJO Framework. revoluční architektura beans. verze

Algoritmizace a programování. Terminálový vstup a výstup

3. Třídy. Základní pojmy objektového programování. Třídy

Funkční objekty v C++.

ADT/ADS = abstraktní datové typy / struktury

Java a Caché IV: Manipulace s objekty

5 Rekurze a zásobník. Rekurzivní volání metody

19. Projekt Adventura

Transkript:

7. Dynamické datové struktury Java poskytuje několik možností pro uložení většího množství dat (tj. objektů či primitivních datových typů) v paměti. S nejjednodušší z nich, s polem, jsme se již seznámili. Pole je struktura velmi jednoduchá na používání, která má následující výhody: pole je ze všech datových struktur nejefektivnější z hlediska ukládání a vybírání dat, pokud však potřebuje provádět složitější operace (např. zajistit, aby prvek byl vložen pouze jednou), může být použití jiných struktur efektivnější, pole zajišťuje typovou kontrolu pokud nadefinujete pole objektů určité třídy, můžete do něho vkládat pouze instance této třídy (a instance potomků této třídy), pole umožňuje vkládat přímo primitivní datové typy, u ostatních datových struktur se musí převést na objekty. Pole má však také dvě velké nevýhody: je třeba předem znát počet prvků, které do něj budete ukládat, nepodporuje některé složitější způsoby práce s objekty (např. vkládání prvků doprostřed pole či vytváření asociativních polí). Java poskytuje pro práci s poli pomocnou třídu Arrays, která nabízí např. třídění pole či prohledávání pole. Kontejnery Při ukládání objektů do dynamických datových struktur (často se pro ně používá pojem kontejner) nemusíme dopředu znát počet vkládaných objektů. Jednotlivé typy kontejnerů dále umožňují složitější operace s daty. Ve zbytku této kapitoly se budeme zabývat kontejnery implementovanými v Javě verze 1.2. Starší verze Javy poskytují jiné dynamické struktury (Vector, Stack, HashTable) tyto zde nebudeme popisovat. Na následujícím obrázku vidíme strukturu dědění tříd včetně zakomponovaných struktur ze starších verzí, na dalším pak už zjednodušenou strukturu pro nové třídy verze 1.2. Tečkovaně jsou znázorněna rozhraní, čárkovaně abstraktní třídy a plnou čarou třídy. 49

Iterator Collection Map ListIterator List Set AbstractMap SortedMap AbstractCollection SortedSet AbstractList AbstractSet HashMap TreeMap HashSet TreeSet WeakHashMap Hashtable (Legacy) Utilities Vector (Legacy) ArrayList AbstractSequentialList Collections Arrays Stack (Legacy) LinkedList Comparable Comparator obrázek 5 Vazby mezi třídami balíku java.util Iterator Collection Map ListIterator List Set HashMap TreeMap WeakHashMap ArrayList LinkedList HashSet TreeSet Utilities Collections Comparable Comparator Arrays obrázek 6 Vazby mezi třídami balíku java.util (pouze pro nové třídy z Java 1.2) Na první pohled vypadá tato struktura velmi složitě, ale po podrobnějším zkoumání zjistíte, že jde vlastně jen o dva druhy tříd kolekce (Collection) a mapy (Map). Rozhraní Collection poskytuje základní metody pro tvorbu a použití seznamů (List) a množin (Set). Seznamy ukládají instance do lineární struktury s opakováním prvků. Obvykle se používá ArrayList, pouze v případě velkého množství vkládání/vyjímání dovnitř/zevnitř seznamu je výhodnější LinkedList. 50

Množiny ukládají pouze instance různých hodnot pro zjištění odlišnosti se používá metoda equals (v praxi se do množiny vkládají instance pouze jedné třídy). Efektivnější je použití varianty HashSet, pouze v případě, že potřebujeme mít seznam hodnot trvale setříděn, používá se TreeSet. Mapy (používá se též pojem asociativní pole) ukládají dvojice instancí - klíč a jemu odpovídající hodnotu. Klíče se v Map nemohou opakovat (obdobně jako v množině). Efektivnější je použití varianty HashMap, varianta TreeMap zajišťuje trvalé setřídění mapy dle klíčů. Rozhraní Iterator umožňuje procházet jednotlivé prvky kolekce bez ohledu na její konkrétní typ (např. když chcete vypsat všechny prvky kolekce). Pokud chceme vkládat instance do struktur, které udržují setříděné prvky (TreeSet a TreeMap), je nutné zajistit porovnání těchto instancí na větší/menší. Toto lze zajistit dvěmi způsoby buď musí mít vkládané instance implementováno rozhraní Comparable s metodou compareto nebo při vytvoření dynamické struktury je nutné zadat implementaci rozhraní Comparator, která umí porovnat vkládané objekty. Třída Collections (neplést s rozhraním Collection) poskytuje další metody pro práci s kontejnery jako je např. třídění, synchronizace datové struktury či zmražení datové struktury. V následujících tabulkách najdete přehled nejpoužívanějších metod jednotlivých rozhraní Rozhraní Collection Metoda užití boolean add(object v) Přidá do kolekce prvek typu Object. Pokud se operace nepodaří vrací false. void clear( ) Zruší všechny prvky v kolekci. boolean contains(object v) Vrací true, jestliže kolekce obsahuje prvek uvedený jako argument metody. boolean isempty( ) Vrací true, jestliže je kolekce prázdná. Iterator iterator( ) Vrací Iterator, pomocí kterého lze projít všechny prvky kolekce. boolean remove(object v) Jestliže kolekce obsahuje argument, je odpovídající prvek zrušen. Vrací true když je prvek zrušen. int size( ) Vrací počet prvků uložených v kolekci. tabulka 17 Metody rozhraní Collection 51

metoda boolean containskey(object k) void clear( ) boolean containsvalue(object v) boolean isempty( ) Collection value( ) Set keyset( ) Set entryset( ) Object put(object k, Object v) int size( ) Object get(object k) Rozhraní Map užití Pokud je klíč obsažen v mapě, vrací true. Zruší všechny prvky v mapě. Vrací true, jestliže mapa obsahuje hodnotu uvedenou jako argument metody. Vrací true, jestliže je mapa prázdná. Vrací kolekci (Collection) hodnot. Vrací množinu (Set) obsahující klíče. Vrací množinu (Set) s prvky Map.Entry. Uloží hodnoty do mapy. Vrací počet prvků uložených v mapě. Vrací hodnotu odpovídající zadanému klíči. tabulka 18 Metody rozhraní Map Metoda boolean hasnext( ) Object next( ) Rozhraní Iterator užití Vrací true, pokud je v kontejneru další prvek. Vrátí následující prvek kontejneru. tabulka 19 Metody rozhraní Iterator Vytvoření kontejneru Všechny kontejnery jsou definovány tak, že jejich prvky jsou instance třídy Object a instance potomků třídy Object (což znamená, že do kontejnerů lze vkládat instance libovolných tříd, neboť v Javě jsou všechny třídy potomky třídy Object). Z toho také vyplývá jedno omezení kontejnerů, nelze do nich ukládat primitivní datové typy - pokud je potřebujeme do kontejneru vložit, musíme je převést do objektové podoby, tedy např. celá čísla na instance třídy Integer. Nejjednodušší způsob vytvoření a vypsání několika kontejnerů je vidět v následujícím příkladě. public class VypisKontejneru { static Collection napln(collection c) { c.add("pes"); c.add("pes"); c.add("kocka"); return c; 52

static Map napln(map m) { m.put("pes", "Alik"); m.put("pes", "Rek"); m.put("kocka", "Mina"); return m; public static void main(string[] args) { System.out.println(napln(new ArrayList())); System.out.println(napln(new HashSet())); System.out.println(napln(new HashMap())); Výsledkem tohoto programu je následující výpis: [pes, pes, kocka] [kocka, pes] {kocka=mina, pes=rek Na prvním řádku je výstup z listu, proto se může stejný řetězec opakovat vícekrát. Na druhém řádku je výpis setu a tedy každý prvek má jedinečnou hodnotu. Pro výpis mapy na třetím řádku platí totéž, klíč, v tomto případě se řetězec "pes", se vyskytuje pouze jednou a to s hodnotou "Rek", která byla vkládána jako druhá. Z toho vyplývá, že v případě vložení klíče, který už v map je, je původní hodnota přepsána novou. Tedy v našem příkladě "Alik" je přepsáno řetězcem "Rek". Pokud potřebujeme do kontejneru vložit čísla, tak jednoduché řešení ukazuje následující příklad: ArrayList cisla = new ArrayList(); for(int i = 0; i < 10 i++) cisla.add(new Integer(i)); Jestliže potřebujeme vytvořit mapu, ve které budou hodnoty také číselné a při každém dalším výskytu stejného klíče se budou měnit, máme dvě možnosti řešení. První je uložit jako hodnotu objektovou reprezentaci čísla a při každé změně jeho hodnoty převádět objekt na jednoduchou proměnnou, provést změnu a znovu převést na objekt (tento způsob je použit v souhrnném příkladě na konci kapitoly). Druhá možnost řešení je vytvořit si vlastní třídu s proměnnou jednoduchého typu a její instance využít jako hodnoty v mapě, viz následující příklad. V něm se vygeneruje 10000 náhodných celých čísel z intervalu 1 až 20 a zjišťuje se, kolikrát bylo které vygenerováno. Vygenerované číslo je klíčem v mapě (převedené na instance třídy Integer), počty výskytů se počítají pomocí vlastní třídy Counter, jejíž instance jsou hodnoty v mapě. class Counter { int pocet = 1; public String tostring() { return Integer.toString(pocet); 53

public class Statistika { public static void main(string[] args) { HashMap hm = new HashMap(); for(int i = 0; i < 10000; i++) { Integer r = new Integer((int)(Math.random() * 20)); if(hm.containskey(r)) ((Counter)hm.get(r)).pocet++; else hm.put(r, new Counter()); System.out.println(hm); Všimněte si, že ve třídě Counter je definována metoda tostring, která se automaticky použije pro výpis hodnoty konkrétní instance v metodě System.out.println. Procházení kontejneru - Iterátor Pro postupné procházení jednotlivých seznamů se používají metody rozhraní Iterator. Rozhraní Collection poskytuje metodu iterator( ), která vytvoří instanci iterátoru. Pomocí metody next( ) vrací Iterator jednotlivé hodnoty (nejdříve první, poté postupně následující). Metoda hasnext( ) zjišťuje, zda následuje další prvek. Pro následující příklady této kapitoly si definujeme dvě jednoduché třídy public class Kocka { private int cislokocky; Kocka(int i) { cislokocky = i; void tisk() { System.out.println("Kocka #" + cislokocky); public class Pes { private int cislopsa; Pes(int i) { cislopsa = i; void tisk() { System.out.println("Pes #" + cislopsa); V následujícím příkladě si ukážeme, jak vytvořit a naplnit ArrayList a jak jeho obsah vypsat pomocí iterátoru. public class Kocky { public static void main(string[] args) { Collection kocky = new ArrayList(); for(int i = 0; i < 7; i++) kocky.add(new Kocka(i)); Iterator it = kocky.iterator(); while(it.hasnext()) ((Kocka)it.next()).tisk(); 54

Všimněte si deklarace kolekce kocky pokud nepotřebujeme v programu specifické vlastnosti konkrétní kolekce, obvykle se deklaruje jako typ Collection. Tento postup umožňuje jednoduchou výměnu typu kolekce, pokud by nevyhovovala. Při výpisu je použito přetypování (Kocka)e.next(), protože jinak není možné použít metodu tisk() třídy Kocka. Po vyjmutí z kolekce nebo mapy je prvek instancí třídy Object a teprve přetypováním zajistíme přiřazení vyjmuté instance ke správné třídě. V tomto se ale může skrývat mnoho chyb, které se projeví až při spuštění programu viz následující příklad. public class KockyAPsi { public static void main(string[] args) { List kocky = new ArrayList(); for(int i = 0; i < 7; i++) kocky.add(new Kocka(i)); //není problem pridat do listu i psa (i on je //potomkem tridy Object) kocky.add(new Pes(7)); for(int i = 0; i < kocky.size(); i++) ((Kocka)kocky.get(i)).tisk(); Na rozdíl od minulého příkladu, kdy pro procházení seznamu byl použit Iterator, zde byla pro procházení seznamu použita metoda get(int i), která vrací hodnotu prvku na zadané pozici v seznamu (a je dostupná pouze pro seznamy List). Instanci třídy lze přetypovat na instanci kteréhokoli z jeho předků nikoli na libovolný typ. Nelze tedy z instance třídy Pes udělat instanci třídy Kocka pokud se o to pokusíte (toto nastane ve výše uvedeném příkladu), vznikne výjimka ClassCastException. Java nám poskytuje operátor instanceof, pomocí kterého lze zjistit, zda instance je instancí zadané třídy. V následujícím příkladě použijeme tento operátor k rozdělení instancí vyjmutých z listu kocky, do kterého uložíme instance tříd Kocka a Pes. public class KockyAPsi2 { Collection zvirata = new ArrayList(); KockyAPsi2() { for (int i =1;i<11;i++) { zvirata.add(new Kocka(i)); zvirata.add(new Pes(i)); 55

void vypis(){ Iterator it = zvirata.iterator(); Object o; while (it.hasnext()) { if ((o=it.next()) instanceof Kocka) ((Kocka)o).tisk(); else ((Pes)o).tisk(); public static void main (String [] args) { KockyAPsi2 kockyapsi = new KockyAPsi2(); kockyapsi.vypis(); Pro výpis hodnot z mapy nelze přímo použít iterátor, ale je nutné převést mapu na kolekci. Rozhraní Map má k tomuto účelu tři metody metodu value(), která vytvoří kolekci hodnot, metodu keyset(), která vytvoří množinu klíčů a metodu entryset( ), pomocí které lze vytvořit z mapy množinu objektů MapEntry obsahujících klíč i příslušnou hodnotu. Pomocí metod getkey( ) a getvalue( ) pak můžeme získat jednotlivé prvky mapy. Následující metoda vypismapy (TreeMap tm) ukazuje možné použití těchto metod pro vypsání obsahu mapy. void vypismapy (TreeMap tm) { Iterator it = tm.entryset().iterator(); while(it.hasnext()) { Map.Entry a = (Map.Entry)it.next(); System.out.println(a.getKey()+"/t/t"+a.getValue); Souhrnný příklad Zadání příkladu: Máme soubor Zvirata.txt: pes Rek kocka Micka kocka Mourek pes Alik morce Smudla morce Fousek kocka Packa pes Bety pes Asta kocka Paty pes Fik Našim úkolem je zjistit, kolik je v souboru záznamů o jednotlivých druzích zvířat. Z každé věty tedy potřebujeme jen její první část, proto použijeme StringTokenizer. Při tvorbě mapy budeme postupovat následovně: pomocí metody containskey() zjistíme, zda už je tato hodnota klíče v mapě uložena. Pokud ano, pomocí metody get() získáme hodnotu odpovídající tomuto klíči a převedeme ji na číslo, 56

přičteme 1 a hodnotu znovu uložíme do mapy. Pokud se klíč ještě v mapě nevyskytuje, uložíme ho do ní s hodnotou Integer(1). Výsledky pomocí iterátoru vypíšeme. import java.io.*; public class TvorbaMapy { public static HashMap vytvor(string jmenosouboru) { LinkedList ln = new LinkedList(); HashMap tm = new HashMap(); try { BufferedReader veta = new BufferedReader(new FileReader(jmenoSouboru)); String s,s1,s2 = " "; while ((s = veta.readline())!= null){ StringTokenizer t = new StringTokenizer(s); s1 = t.nexttoken(); s2= t.nexttoken(); if (tm.containskey(s1)) { int i = ((Integer)tm.get(s1)).intValue(); tm.put(s1,new Integer(++i)); else { tm.put(s1, new Integer(1)); ; veta.close(); catch (FileNotFoundException e1) { System.out.println("Soubor nenalezen"); catch (IOException e2 ) { System.out.println("Chyba pri cteni"); return tm; public static void tiskmapy( HashMap m){ Iterator it = m.entryset().iterator(); while(it.hasnext()) { Map.Entry a = (Map.Entry)it.next(); System.out.println(a.getKey()+"\t"+a.getValue()); public static void main (String []args) { tiskmapy(vytvor("zvirata.txt")); 57