Aplikacje w Javie wykład 8 Kolekcje c.d. (komparatory, kolejki, mapy)

Podobne dokumenty
Programowanie w Javie - wykład 13 Kolekcje c.d. ( mapy)

Aplikacje w Javie wykład 8 Kolekcje c.d. (komparatory, kolejki, mapy)

Programowanie w Javie- wykład 12 Kolekcje (zbiory)

Programowanie w języku Java. Kolekcje

Apilkacje w środowisku Java - wykład 8 Kolekcje c.d. ( mapy) Podstawy programowania funkcyjnego

java.util.* :Kolekcje Tomasz Borzyszkowski

import java.util.*; public class ListExample { public static void main(string args[]) { List<String> lista1= new ArrayList<String> ();

Java Collections Framework

Realizacja ekstensji klasy. Paulina Strzelecka, Tomasz Roszkowski

Programowanie obiektowe

Kolekcje. Na podstawie:

Kolekcje mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

Programowanie Obiektowe (Java)

Kolekcja (kontener) to po prostu obiekt, który grupuje wiele elementów w jeden twór.

Kolekcje - pakiet Java Collections Framework

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

Programowanie w Javie- wykład 11 Kolekcje (listy)

Programowanie w środowisku graficznym- wykład 7 Kolekcje (listy, zbiory)

Tworzenie aplikacji w języku Java

Podstawy otwartych języków programowania Przechowywanie danych

Kurs programowania. Wykład 9. Wojciech Macyna

Programowanie i projektowanie obiektowe

Kolekcje - pakiet Java Collections Framework

Język JAVA podstawy. Wykład 4, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Lista, Stos, Kolejka, Tablica Asocjacyjna

Języki i metody programowania Java INF302W Wykład 3 (część 2)

Dawid Gierszewski Adam Hanasko

Dokumentacja do API Javy.

Wykład 4. Klasa List Kolejki Stosy Słowniki

Programowanie w środowisku graficznym- wykład 8 Kolekcje c.d. ( mapy) Strumienie Wątki-podstawy

Wydział Fizyki i Informatyki Stosowanej, Uniwersytetu Łódzkiego Łódź. Java podstawy języka, wykład 4 1

Java SE Laboratorium nr 7. Temat: Kolekcje

Aplikacje w Javie- wykład 11 Wątki-podstawy

Kolekcje. object that groups multiple elements into a single unit

KOLEKCJE JAVY API: NAJPROSTSZE PODSTAWY

TYPY GENERYCZNE (GENERICS)

dr inż. Piotr Czapiewski Tworzenie aplikacji w języku Java Laboratorium 1

Comparable<Klasa_uzytkownika>

Programowanie obiektowe

Klasy i obiekty cz II

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Java niezbędnik programisty spotkanie nr 8. Kolekcje c.d.

Wykład 5 Wybrane zagadnienia programowania w C++ (c.d.)

Platformy Programistyczne Podstawy języka Java

Instrukcja 2 Laboratorium z Podstaw Inżynierii Oprogramowania

Języki i techniki programowania Ćwiczenia 4 Wzorce

PHP: bloki kodu, tablice, obiekty i formularze

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

Programowanie w Javie - wykład 14 Podstawy programowania funkcyjnego

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

jlabel: void setalignment(label.center/left/right) - wyrównanie String gettext() pobiera aktualny tekst napisu void settext(string text) ustawia

Java niezbędnik programisty spotkanie nr 9. Java 2 Platform, Standard Edition 5.0

GUI - projektowanie interfejsów cz. II

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

Języki i metody programowania Java INF302W Wykład 2 (część 1)

Zadanie polega na stworzeniu bazy danych w pamięci zapewniającej efektywny dostęp do danych baza osób.

WYJĄTKI, KOLEKCJE ZAGADNIENIA: 1. Wyjątki, 2. Kolekcje, vector, hashtable, properties, Klasy Arrays i Collections.

Wykład 7: Pakiety i Interfejsy

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Wybrane algorytmy tablicowe

Aplikacje w Javie wykład 7 Kolekcje (listy, zbiory)

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Wykład 8: Obsługa Wyjątków

Kiedy potrzebne. Struktura (rekord) Struktura w języku C# Tablice struktur. struktura, kolekcja

2. Tablice. Tablice jednowymiarowe - wektory. Algorytmy i Struktury Danych

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

Klasy abstrakcyjne, interfejsy i polimorfizm

Kolekcje w Javie cz. 1

Laboratorium z przedmiotu: Inżynieria Oprogramowania INEK Instrukcja 7

Rozdział 4 KLASY, OBIEKTY, METODY

Java: interfejsy i klasy wewnętrzne


Polimorfizm, metody wirtualne i klasy abstrakcyjne

Wątki. Definiowanie wątków jako klas potomnych Thread. Nadpisanie metody run().

API STREAM WYRAŻENIA LAMBDA

Laboratorium z przedmiotu: Inżynieria Oprogramowania INEK Instrukcja 6

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

Wykład 3 Składnia języka C# (cz. 2)

Języki i techniki programowania Ćwiczenia 2

Algorytmy i Struktury Danych.

Klasy abstrakcyjne i interfejsy

Programowanie obiektowe

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Wyjątki. Streszczenie Celem wykładu jest omówienie tematyki wyjątków w Javie. Czas wykładu 45 minut.

Java Zadanie 1. Aby poprawnie uruchomić aplikację desktopową, należy zaimplementować główną metodę zapewniającą punkt wejścia do programu.

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Programowanie obiektowe. Literatura: Autor: dr inŝ. Zofia Kruczkiewicz

Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04

Listy, krotki, słowniki, funkcje

Język Java część 2 (przykładowa aplikacja)

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

Dziedziczenie. Tomasz Borzyszkowski

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Programowanie i struktury danych

Programowanie obiektowe

Typy sparametryzowane

Kolekcje obiektów. Wyj tki.

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Algorytmy i Struktury Danych. Anna Paszyńska

Kontenery i iteratory. Wykorzystanie kontenerów w praktyce.

Transkrypt:

Aplikacje w Javie wykład 8 1 Kolekcje c.d. (komparatory, kolejki, mapy) Treści prezentowane w wykładzie zostały oparte o: Barteczko, JAVA Programowanie praktyczne od podstaw, PWN, 2014 http://docs.oracle.com/javase/8/docs/ C. S. Horstmann, G. Cornell, Java. Podstawy, Helion, Gliwice 2008

Kolejki 2 W kolejkach - w przeciwieństwie do list - nie możemy bezpośrednio sięgać po elementy na dowolnych pozycjach, ani też wstawiać elementów na dowolnych pozycjach. Kolejka (ang. queue) jest sekwencją elementów, na której operacje wstawiania, pobierania i usuwania elementów są możliwe tylko w określonym porządku. Najczęściej porządkiem tym jest FIFO first in first out, co odpowiada intuicyjnemu pojmowaniu kolejki: pierwszy przyszedł, pierwszy zostanie obsłużony. W kolejkach FIFO możliwe są tylko następujące operacje: wstawienie elementu na końcu liniowej sekwencji elementów, pobranie elementu z początku liniowej sekwencji elementów, usunięcie elementu z początku liniowej sekwencji elementów. Kolejki LIFO (inaczej zwane stosami - ang. stack) last in first out : ten kto przyszedł ostatni, będzie obsłużony pierwszy, dopuszczają operacje: wstawienie elementu na początku liniowej sekwencji elementów, pobranie elementu z początku liniowej sekwencji elementów, usunięcie elementu z początku liniowej sekwencji elementów. Kolejki z priorytetami pobierają i usuwają elementy z początku, ale początek określany jest przez kryteria porównywania obiektów.

Kolejki 3 Wszystkie kolejki oferują następujące operacje, określane przez interfejs Queue. Operacja Metody zgłaszające wyjątki przy braku miejsca lub elementów Metody nie zgłaszające wyjątków przy braku miejsca lub elementów Wstawianie boolean add(t e) zwraca true, gdy sukces lub zgłasza wyjątek IllegalStateException przy braku miejsca Usuwanie Pobieranie bez usuwania T remove() zwraca usunięty z początku element typu T lub zgłasza wyjątek: NoSuchElementException jeśli kolejka jest pusta T element() zwraca element z początku lub zgłasza wyjątek: NoSuchElementException jeśli kolejka jest pusta boolean offer(t e) zwraca: true - gdy sukces, false - gdy nie ma miejsca. Zgłasza wyjątek IllegalArgumentException, jesli nie dopuszcza wstawienia danego rodzaju elementu T poll() zwraca usunięty z początku element typu T lub null, jesli kolejka jest pusta T peek() zwraca element z początku lub null, jesli kolejka jest pusta

Kolejki podwójne Kolejka podwójna (ang. deque) jest liniową sekwencją elementów, na której operacje wstawiania, pobierania i usuwania elementów są możliwe na obu końcach sekwencji. Operacje na kolejce podwójnej są określane przez interfejs Deque, który dodaje do Queue metody Wstawianie Usuwanie Pobieranie bez usuwania Z wyjątkiem przy braku miejsca lub elementów boolean addfirst(e) boolean addlast(e) T removefirst() T removelast() T getfirst() T getlast() Bez tego wyjątku boolean offerfirst(e) boolean offerlast(e) T pollfirst() T polllast() T peekfirst() T peeklast() Wśród gotowych implementacji kolejek w JCF są dostępne następujące klasy: LinkedList - lista, która jest jednocześnie kolejką podwójną (bez ograniczeń na miejsce), ArrayDeque - kolejka podwójna, zrealizowana jako rozszerzalna tablica PriorityQueue - kolejka z priorytetami. 4

Kolejki - przykład 5 import java.util.*; public class Kolejki { public static void main(string[] args) { ArrayDeque<Integer> deq = new ArrayDeque<>(); // Działa jak kolejka FIFO deq.offer(1); deq.add(20); deq.add(12); System.out.println("FIFO: " + deq);//fifo: [1, 20, 12] System.out.println("Podglądamy (z początku): " + deq.peek());//podglądamy (z początku): 1 // Działa jak kolejka LIFO deq.clear(); deq.addfirst(1); deq.addfirst(20); deq.addfirst(12); System.out.println("LIFO: " + deq);//lifo: [12, 20, 1] System.out.println("Podglądamy (z początku): " + deq.peek());//podglądamy (z początku): 12

Kolejki - przykład 6 System.out.println("Możemy używać iteratora"); for (Integer i : deq) System.out.println(i); // Kolejka z priorytetami PriorityQueue<Integer> pq = new PriorityQueue<>(); pq.add(1); pq.add(20); pq.add(12); System.out.println("PRIOR QUE: " + pq); //PRIOR QUE: [1, 20, 12] System.out.println("Z kolejki z priorytetami" +" pobieramy kolejne elementy zawsze z początku"); System.out.println("Początek-domyślnie najmniejszy " + "element"); Integer n; while((n = pq.poll())!= null) { System.out.println(n);// 1 12 20

Porównywanie obiektów 7 Dodawanie elementów do zbiorów uporządkowanych (np. TreeSet), iterowanie po kolekcjach uporządkowanych, pobieranie elementów z kolejek z priorytetami, a także sortowanie kolekcji i tablic algorytmami kolekcyjnymi odbywa się w oparciu o: naturalny porządek obiektów, lub reguły porównywania obiektów określane przez komparator - obiekt klasy implementującej interfejs Comparator. Naturalny porządek określany jest przez implementację interfejsu Comparable<T> w klasie obiektów i dostarczenie definicji metody compareto tego interfejsu, porównującej dwa obiekty typu T. Metoda compareto ma następującą sygnaturę: public int compareto(t otherobject) gdzie T jest typem obiektu, i zwraca: liczbę < 0, jeżeli ten obiekt (this) znajduje się (w porządku) przed obiektem otherobject, liczbę > 0, jeżeli ten obiekt (this) znajduje się (w porządku) po obiekcie otherobject, 0, jeśli obiekty są takie same.

Porównywanie obiektów Wiele standardowych klas Javy implementuje interfejs Comparable (np. klasa String, Date, klasy opakowujące typy proste). We własnych klasach musimy o to zadbać sami. Zatem, każda nasza klasa, której obiekty mogą być elementami kolekcji uporządkowanych powinna określać naturalny porządek swoich obiektów. Np. w klasie Worker moglibyśmy ustalić, że naturalną kolejnością obiektów jest alfabetyczna kolejność nazwisk w porządku rosnącym poprzez dostarczenie definicji metody compareto: class Worker implements Comparable<Worker> { private String lname; private String fname; private int salary; //... public int compareto(worker other) { int res = lname.compareto(other.lname); if(res==0) res = fname.compareto(other.fname) return res; public String tostring() { return fname + " " + lname + " " + salary; 8

Porównywanie obiektów Po wykorzystaniu powyższej klasy w poniższym fragmencie: Worker[] p = { new Worker("Jan", "Kowalski", 1000), new Worker("Jan", "Malinowski", 1200), new Worker("Jan", "Kowalski", 1400), new Worker("Jan", "Kowalewski", 2000), ; Set<Osoba> set = new TreeSet<>(); for (Worker w : p) set.add(w); System.out.println(set); otrzymamy (naturalnie) uporządkowany wynik: [Jan Kowalewski 2000, Jan Kowalski 1000, Jan Malinowski 1200] 9

Porównywanie obiektów - komparator Aby uzyskać zbiory uporządkowane według różnych kryteriów, np. według nazwisk lub według pensji, albo porządek odwrotny (np. w malejącym alfabetycznym porządku nazwisk) należy skorzystać z komparatora. Komparator jest obiektem porównującym inne obiekty. Komparatory w Javie są realizowane jako obiekty klas implementujących interfejs Comparator<T>, gdzie T jest typem porównywanych obiektów Interfejs ten zawiera metodę: int compare(t o1, T o2) której implementacja winna porównywać dwa swoje argumenty i zwracać: wynik 0 jeśli oba obiekty są równe, liczbę mniejszą od 0, jeśli o1 jest "mniejszy" od o2 liczbę większą od 0, jeśli o1 jest "większy" od o2. Obiekt komparator może być podany jako argument konstruktora klasy TreeSet (wtedy to on, a nie naturalny porządek, decyduje o sposobie wpisywania elementów do zbioru), może rownież występować jako argument metod sortowania z klasy Collections i Arrays, konstruktorów klasy PriorityQueue oraz konstruktorów innych kolekcji uporządkowanych (np. TreeMap). Specjalny komparator, odwracający naturalny porządek, uzyskiwany jest za pomocą statycznej metody klasy Collections - Collections.reverseOrder(). 10

Porównywanie obiektów - przykład 11 import java.util.*; class Worker implements Comparable<Worker> { private String lname; private String fname; private int salary; public Worker(String fn, String ln, int sal) { lname = ln; fname = fn; salary = sal; public int getsalary() { return salary; public int compareto(worker other) { int res = lname.compareto(other.lname); if(res==0) res = fname.compareto(other.fname); return res; public String tostring() { return fname + " " + lname + " " + salary;

Porównywanie obiektów - przykład 12 public class TestKomparator { public static void main(string args[]) { Worker[] workers = { new Worker("Jan", "Kowalski", 1000), new Worker("Jan", "Malinowski", 1200), new Worker("Jan", "Kowalski", 1400), new Worker("Jan", "Kowalewski", 2000), new Worker("Stefan", "Zwierz", 2000), ; // Ziór uporządkowany wg porządku naturalnego //(rosnąca kolejność nazwisk) Set<Worker> set = new TreeSet<>(); for (Worker w : workers) set.add(w); System.out.println(set); //[Jan Kowalewski 2000, Jan Kowalski 1000, // Jan Malinowski 1200, Stefan Zwierz 2000]

Porównywanie obiektów - przykład 13 // Zbiór uporządkowany wg rosnących pensji Set<Worker> set2 = new TreeSet<>( new Comparator<Worker>() { public int compare(worker o1, Worker o2) { // różnica pensji wystarczy do porownania: return o1.getsalary() - o2.getsalary(); ); for (Worker w : workers) set2.add(w); System.out.println(set2); //[Jan Kowalski 1000, Jan Malinowski 1200, // Jan Kowalski 1400, Jan Kowalewski 2000] // Zbiór uporządkowany w malejącym porządku naturalnym //(malejące nazwiska) Set<Worker> set3 = new TreeSet<>(Collections.reverseOrder()); for (Worker w : workers) set3.add(w); System.out.println(set3); //[Stefan Zwierz 2000, Jan Malinowski 1200, //Jan Kowalski 1000, Jan Kowalewski 2000]

Porównywanie obiektów 14 W powyższym przykładzie w klasie Worker nie definiowaliśmy metody equals() ani hashcode(), gdyż TreeSet ich nie potrzebuje. Zwróćmy uwagę, że komparator przesądza nie tylko o kolejności iterowania po zbiorze, ale również o tym, czy obiekty uznawane są za różne i czy wobec tego mogą stanowić elementy zbioru (przykład z powtórzeniem nazwiska Jan Kowalski i utrata Stefana Zwierza, gdy komparator porównywał pensje), Należy pamiętać, że: Operacje na zbiorze HashSet używają (i wymagają) metod hashcode() i equals(), a dodawanie i wyszukiwanie elementów odbywa się w oparciu o wyniki metody equals(). Operacje na zbiorze TreeSet odbywają się w oparciu o wyniki metody compareto() lub metody compare() komparatora. Oczywiście, nasze klasy powinny być przygotowane do tego, że ich obiekty mogą znaleźć się w zbiorach typu HashSet albo TreeSet, zatem powinny definiować wszystkie wspomniane metody. A do tego w sposob spójny: jeśli equals zwraca true, to compareto winno zwracać zero, a hashcode te same wartości dla obu porównywanych obiektów.

Porównywanie obiektów 15 Warto zauważyć, że dla zbiorów uporządkowanych (naturalnie lub za pomocą komparatora) dostępna jest możliwość uzyskiwania "pierwszego" lub "ostatniego" (w zdefiniowanym porządku) elementu, a także podzbiorów, które zawierają elementy "od" - "do" (w zdefiniowanym porządku). Służą temu metody interfejsu SortedSet (pośrednio implementowanego przez TreeSet) m.in. headset(), tailset() i subset(), Interfejs NavigableSet bezpośrednio implementowany przez TreeSet dostarcza dodatkowych metod m.in. higher(e e), lower(e e), ceiling(e e), floor(e e), które pozwalają uzyskiwać elmenty "bliskie" względem danego porządku. W klasie TreeSet nie mamy co prawda możliwości dynamicznych zmian komparatora (już po utworzeniu obiektu), ale dzięki dostępowi do obiektu-komparatora (metoda comparator()) i odpowiedniej konstrukcji jego klasy możemy zmieniać charakterystyki jego działania niejako "w locie". Uwaga. Implementacja metody compareto w klasie String nie uwzględnia właściwego porządku napisów w różnych językach. Aby właściwie sortować napisy należy użyć kolatora (pobieramy go statyczną metodą Collator.getInstance()), który implementuje interfejs Comparator i jest specjalnym komparatorem uwzględniającym lokalizację (regionalne ustawienia językowe). List<String> polskie = Arrays.asList("z", "ę", "ź", "a", "ą"); Collections.sort(polskie, Collator.getInstance(new Locale("pl"))); // od Javy 8 mamy metodę sort również w interfejsie List : //list.sort(new Comparator<T>() {..)

Mapy (interfejs Map) 16 Tablica asocjacyjna jest zestawem elementów, do których zapewniono swobodny, bezpośredni (czyli bez konieczności przeglądania elementów zestawu) dostęp za pomocą kluczy. Można sobie wyobrażać, że jest to uogólnienie zwykłej tablicy: w zwykłej tablicy kluczami są indeksy całkowite, a w tablicy asocjacyjnej kluczami mogą być dowolne obiekty. Efektywne realizacje tablic asocjacyjnych opierają się na odpowiedniej implementacji słowników (odwzorowujących klucze w odpowiadające im wartości). W Javie słownikowe implementacje tablic asocjacyjnych określane są słowem map, pochodzącym od terminu mapping, oznaczającego jednoznaczne odwzorowanie (w tym przypadku zbioru kluczy w zbiór wartości). Również po polsku krótko będziemy nazywać tablice asocjacyjne mapami.

Mapy Mapa jest jednoznacznym odwzorowaniem zbioru kluczy w zbiór wartości. O mapach możemy myśleć jako o takich kolekcjach par: klucz - wartość, które zapewniają odnajdywanie wartości związanej z podanym kluczem. Zarówno klucze, jak i wartości mogą być referencjami do dowolnych obiektów (jak również wartościami null). Oczywiście, wartości kluczy nie mogą się powtarzać (odwzorowanie musi być jednoznaczne). Natomiast pod różnymi kluczami można zapisać te same wartości (odwzorowanie nie musi być wzajemnie jednoznaczne). Przykłady: Zestaw danych zawierający: Klucz: NIP ->Wartość : Dane o podatniku Klucz: Kraj ->Wartość : Stolica Klucz: Nazwa kontaktu ->Wartość : Numer telefonu i pozwalający na odnajdywanie poszukiwanej wartości po kluczu. Istotą zastosowania map jest możliwość łatwego i jednocześnie szybkiego odnajdywania informacji w powiązanych zestawach danych. 17

Mapy - interfejs Map<K,V> 18 W JCF ze względu na specyfikę działania na mapach (jako zestawach par kluczewartości) implementują one interfejs Map, a nie Collection. Podstawowe operacje na mapie (niezależnie od jej implementacji) są określone przez metody interfejsu Map<K,V>, gdzie K typ kluczy, V typ wartości. Należą do nich: void clear() - usuwa wszystkie elementy mapy (operacja opcjonalna). boolean containskey(object key) - zwraca true jeżeli mapa zawiera podany klucz (i związaną z nim wartość). boolean containsvalue(object value) - zwraca true jeśli mapa zawiera podaną wartość (do ktorej może prowadzić wiele kluczy). V get(object key) - zwraca wartość dla danego klucza. V put(k key, V value) - dodaje parę klucz-wartość do mapy (lub zastępuje poprzednie odwzorowanie tego klucza podaną wartością), zwraca obiekt, który uprzednio znajdował się pod danym kluczem lub null. void putall(map<...> t)- dodaje do mapy wszystkie elementy mapy t. V remove(object key) - usuwa odwzorowanie: klucz-wartość z tej mapy (operacja opcjonalna). Zwraca usuniętą wartość. int size() - zwraca liczbę par klucz-wartość w mapie Uwaga. Metody put..., ze względu na jednoznaczność, powodują zastąpienie istniejących w mapie odwzorowań, które mają takie same klucze jak dodawane odwzorowania.

Mapy - interfejs Map<K,V> Metoda containskey(..) wydaje się duplikować jakby działanie metody get(..). Ale pamietajmy, że mapy mogą zwierać pod danym kluczem wartość null. Wtedy metoda get zwraca null i nie wiemy czy oznacza to brak odwzorowania w mapie, czy też odwzorowanie klucza w wartość null. Metoda containskey pozwala to rozstrzygnąć: zwróci true jeśli odwzorowanie jest (nawet jeśli wartością "pod" kluczem jest null), natomiast zwrócona wartość false świadczy na pewno o tym, że w mapie nie ma poszukiwanego odwzorowania. Ponadto w interfejsie Map istnieją metody : Set<K> keyset() - zwraca widok na zbiór kluczy. Collection<V> values() - zwraca widok na kolekcję wartości. Set<Map.Entry<K,V>> entryset() - zwraca widok na zbiór par kluczewartości jako zbiór obiektów typu Map.Entry<K,V>. K jest typem klucza, V - typem wartości. Ze względu na to, że mapy nie implementują interfejsu Collection, nie można od nich uzyskać bezpośrednio iteratorów. Można natomiast uzyskać kolekcje, które są widokami na zestawy kluczy (Set, bo bez powtórzeń), zestawy wartości (Collection, bo bez porządku i z możliwością powtórzeń elementów), oraz par klucze-wartośći (Set, bo bez powtórzeń). Od tych kolekcji - naturalnie - możemy uzyskać iteratory. 19

Mapy - interfejs Map<K,V> 20 Uwaga: przy tworzeniu map podajemy dwa argumenty typu (typ klucza i typ wartości) np. Map<String, Integer>. W tym przypadku metoda get(...) zwróci obiekt właściwego typu i nie będą potrzebne konwersje zawężające. Jesli używamy surowego typu Map lub argumenty typu są Object, to metoda get(...) będzie zwracać Object, zatem uzyskując wartość "po kluczu" musimy dokonać konwersji zawężającej do odpowiedniego typu. Hierarchia interfejsów i klas w JCF: Interface: Map SortedMap NavigableMap HashMap WeekHashMap LinkedHashMap TreeMap

Mapy konkretne implementacje W mapach istotne jest szybkie odnajdywanie kluczy. Klucze (poprzez umieszczenie ich razem z wartościami w odpowiednich strukturach danych) związane są już bezpośrednio i niejako natychmiastowo z wartościami. Podobnie zatem jak w przypadku zbiorów istnieją dwie podstawowe implementacje, pozwalające na szybkie wyszukiwanie kluczy - implementacja oparta na tablicy mieszającej (HashMap) oraz na drzewie czerwono-czarnym (TreeMap). Niejako ubocznym skutkiem zastosowania tej ostatniej jest możliwość przeglądania kluczy mapy w naturalnym porządku rosnącym lub w porządku, określanym przez komparator podany przy konstrukcji mapy. Reguły stosowania komparatorów są takie same jak w przypadku zbiorów uporządkowanych. Mówimy, że mamy do czynienia z mapą uporządkowaną, a zatem mamy - tak jak w przypadku zbiorów uporządkowanych - dodatkowe właściwości takie jak "pierwszy" i "ostatni" klucz lub ich podzbiór "od" - "do" (określane przez metody interfejsu SortedMap) oraz operacje znajdowania bliskich kluczy (z interfejsu NavigableMap) Implementacja LinkedHashMap pozwala na odtworzenie kolejności dodawania elementów do mapy. 21

Mapy HashMap przykład 1 Przykład. Wyobraźmy sobie, że w pliku znajdują się nazwy i adresy firm. Nasz program po wczytaniu pliku ma za zadanie dostarczenie prostego interfejsu wyszukiwania adresu dla podanej nazwy firmy. Zauważmy, że proste sposoby rozwiązania tego problemu (np. prowadzenie dwóch list - nazw i adresów - i liniowe wyszukiwanie nazwy na liście nazw po to by otrzymać pozycję adresu na liście adresów) są bardzo nieefektywne. Klasa HashMap, która reprezentuje zestaw par klucz-wartość, efektywnie pozwala rozwiązać ten problem. Zatem jeśli postać pliku wejściowego jest następująca: nazwa_firmy1 adres nazwa_firmy2 adres... nazwa_firmyn adres to problem wyszukiwania adresów dla firm podawanych w dialogach wejściowych można oprogramować w następujący sposób: 22

import java.util.*; import java.io.*; import javax.swing.*; Mapy HashMap przykład 1 class TestMap1 { public static void main(string args[]) throws IOException { // mapa odwzorowań : nazwa -> adres // argumenty typu są dwa: dla klucza(nazwy) i wartości(adresu) Map<String, String> map = new HashMap<>(); // Wczytywanie danych Scanner scan = new Scanner(new File("firmsAddr.txt")); String firmname; String address; while (scan.hasnextline()) { firmname = scan.nextline(); address = scan.hasnextline()? scan.nextline() : ""; //nazwa firmy to klucz pod którym w mapie będzie jej adres map.put(firmname, address); //dodanie pary klucz-wartość do mapy 23

Mapy HashMap przykład 1 // Interakcyjna część programu: dla podanej w dialogu // nazwy firmy pokazywany jest jej adres while ((firmname = JOptionPane.showInputDialog ("Nazwa firmy"))!= null) { address = map.get(firmname); if (address == null) address = "?"; JOptionPane.showMessageDialog(null, "Firma: " + firmname + '\n' + "Adres: " + address); w Javie 8 dla map dostępna jest metoda: getordefault(key, value), która zwraca wartość pod kluczem key, a jeśli jej nie ma (w mapie brak klucza i wartości z nim związanej), to zwraca domyślną wartość value. Używając tej metody powyższy fragment programu mógłby wyglądać następująco: while ((firmname = JOptionPane.showInputDialog ("Nazwa firmy"))!= null) { address = map.getordefault(firmname, "?"); JOptionPane.showMessageDialog(null, "Firma: " + firmname + '\n' + "Adres: " + address); 24

Mapy keyset() i values() przykład 1 25 Metody keyset() i values() zwracają odpowiedni zbiór kluczy i kolekcję wartości mapy. Iterując po kluczach możemy sięgać po kolejne pary klucz-> wartość. Dla mapy map firma-> adres: System.out.println(map); Set<String> keys = map.keyset(); System.out.println("Klucze: " + keys); Collection<String> vals = map.values(); System.out.println("Wartości: " + vals); Set entries = map.entryset(); System.out.println("Pary: " + entries); for (String name : map.keyset()){ String addr = map.get(name); System.out.println(name + " - adres: " + addr); Uzyskana poprzez keyset() kolekcja kluczy jest widokiem (a nie jakimś nowym obiektem-zbiorem) na klucze mapy, co oznacza, że operując na elementach tego zbioru faktycznie operujemy na kluczach mapy. Podobne widoki są zwracane poprzez values() i entryset(). Widok na klucze jest typu wyznaczanego przez klasę implementującą interfejs Set, ale jest to inna klasa niż znane nam HashSet i TreeSet

Mapy TreeMap przykład 1 Parę klucz-> wartość w mapie można usunąć za pomocą metody remove(key) albo za pomocą metody remove() iteratora zbioru kluczy. System.out.println(map); map.remove("microsoft"); System.out.println(map); map.put("microsoft", "Redmont"); System.out.println(map); for (Iterator<String> it = map.keyset().iterator(); it.hasnext(); ) { if(it.next().equals("microsoft")) it.remove(); Jeśli chcemy mieć posortowane klucze tworzymy TreeMap //mapa w porządku naturalnym (klucze alfabetycznie) Map<String, String> mapsort = new TreeMap<>(map); // Pusta mapa z komparatorem odwracającym // naturalny porządek Map<String, String> mapro = new TreeMap<>(Collections.reverseOrder()); // Wpisujemy do niej pary z mapy map mapro.putall(map); System.out.println(mapRo); 26

Mapy entryset - przykład 2 27 Efektywniejszym od keyset()(gdyż keyset wymaga każdorazowego pobierania wartości spod klucza) sposobem przeglądania map jest użycie metody entryset. Uzyskana kolekcja par klucze-wartości (widok na kluczewartości) jest typu wyznaczanego przez klasę implementującą interfejs Map.Entry (wewnętrzny interfejs interfejsu Map). Dzięki temu - ale tylko w trakcie iteracji po elementach tej kolekcji - mamy dodatkowe możliwości działania, a mianowicie: uzyskanie klucza (metoda getkey()), wartości dla danej pary (metoda getvalue()) oraz zmianę wartości dla danej pary (metoda setvalue(object). Przykład: Map<String, Integer> map = new HashMap<>(); for(char c = 'a'; c <= 'd'; c++) map.put("" + c, (int) c); Set<Map.Entry<String, Integer>> entries = map.entryset(); System.out.println(entries); //Efektywny sposób przeglądania map for(map.entry<string, Integer> e : entries) { Integer val = e.getvalue(); System.out.println(e.getKey() + " " + val); e.setvalue(val + 1000);

Mapy przykład 3 Rozważmy napisanie programu diagnostycznego (np. jakichś napraw), który na podstawie wprowadzonego przez użytkownika symptomu podaje niezbędną akcję do wykonania. Możemy to zrobić np. przy pomocy istrukcji switch i ilością case odpowiadającą ilości symptomów (np. 100). Takie rozwiązanie jest niefektywne, wszelkie zmiany powodują konieczność modyfikacji kodu i ponownej kompilacji. Lepiej będzie oddzielić dane od kodu źródłowego (zapisać je w pliku np. w kolejnych wierszach symptom-akcja) i zastosować mapę. HashMap<String, String> diagmap = new HashMap<>(); for (Scanner sc = new Scanner(new File("diagnose.txt")); sc.hasnextline(); ) { diagmap.put(sc.nextline(), sc.nextline()); String symptom = showinputdialog("podaj symptom"); String act = diagmap.containskey(symptom)? diagmap.get(symptom) : "Nieznany symptom"; showmessagedialog(null, act); Zamiast wielu linijek kodu mamy kilka, a program jest niezależny od zestawu symptomów i odpowiednich akcji. Istotą zastosowania map jest możliwość szybkiego odnajdywania informacji w powiązanych zestawach danych, a także tworzenia zwięzłego i elastycznego kodu. 28

Klasa Collections i Arrays 29 JCF zawiera dwie użyteczne klasy, wspomagające działania na kolekcjach (a także tablicach) - klasę Collections i klasę Arrays. Klasa Collections dostarcza statycznych metod, operujących na kolekcjach lub zwracających kolekcje. Do metod tych należą: algorytmy kolekcyjne, metody zwracające widoki kolekcji, metody tworzące specjalne kolekcje. Wybrane algorytmy kolekcyjne: static int binarysearch(list<... > list, K key)- wyszukiwanie binarne na liście. Zwraca indeks elementu, który jest równy podanemu obiektowi key (wedle metody compareto z klas(y) elementów listy, przy czym zarówno elementy listy, jak i obiekt key muszą być porównywalne). Lista musi być najpierw posortowana według naturalnego porządku np. metodą Collections.sort(List l). Przy braku elementu zwraca wartość < 0 (a nie -1!) static int binarysearch(list<...> list, K key, Comparator<...> c) - wyszukiwanie binarne według podanego komparatora (porównywanie obiektu key z elementami listy następuje za pomocą metody compare komparatora). Lista musi być posortowana zgodnie z porządkiem definiowanym przez komparator np. metodą Collections.sort(List l, Comparator c). Zwraca indeks odnalezionego elementu lub wartość < 0, gdy brak jest elementu.

Klasa Collections i Arrays 30 static void sort(list<t> list)- sortowanie listy według naturalengo porządku elementów (rosnąco) static void sort(list<t> list, Comparator<...> c) - sortowanie listy wedle porządku określanego przez komparator. W klasie Arrays dostarczono statycznych metod, m.in. implementujących algorytmy sortowania i binarnego wyszukiwania dla tablic zarówno typów pierwotnych, jak i obiektów (w tym ostatnim przypadku również z możliwością użycia komparatorów). Metody klasy Collections zwracające widoki kolekcji nakładają na kolekcje "opakowania" w celu zmiany ich niektórych właściwości. W szczególności możemy uzyskać niemodyfikowalne oraz synchronizowane widoki dowolnej kolekcji. Przypomnijmy, że niemodyfikowalność kolekcji oznacza niemożliwość wszelkich modyfikacji (zarówno strukturalnych - dodawanie usuwanie elementów, jak i zmian wartości elementów). Czasami jest to potrzebne, gdy chcemy zagwarantować, by po utworzeniu kolekcja w ogóle nie mogła podlegać zmianom, lub gdy chcemy zróżnicować prawa dostępu do kolekcji dla różnych grup użytkowników naszej aplikacji (ktoś może zmieniać kolekcje, kto inny - tylko przeglądać). Gotowe implementacje kolekcji w JCF są modyfikowalne.

Klasa Collections i Arrays 31 Niemodyfikowalne widoki kolekcji możemy uzyskać za pomocą metod (dla ułatwienia pominięto parametry typu): public static Collection unmodifiablecollection(collection c); public static Set unmodifiableset(set s); public static List unmodifiablelist(list list); public static Map unmodifiablemap(map m); public static SortedSet unmodifiablesortedset(sortedset s); public static SortedMap unmodifiablesortedmap(sortedmap m); Szczególnym rodzajem niemodyfikowalności jest brak możliwości zmian rozmiaru kolekcji. Takich kolekcji (których elementy możemy zmieniać, ale nie możemy do nich dodawać nowych elementów ani usuwać istniejących) dostarcza statyczna metoda aslist z klasy Arrays, zwracająca listę, elementami której są elementy tablicy przekazanej jako argument. Uwaga: lista ta jest widokiem na tablicę! Metoda ta może służyć nie tylko do łatwego tworzenia list z istniejących tablic, ale również do tworzenia nowych, pustych list o niezmiennych rozmiarach: // Tworzy listę o ustalonym (i niezmiennym) // rozmiarze 1000 elementów List l = Arrays.asList(new Object[1000]);

Klasa Collections i Arrays 32 Gotowe kolekcje w JCF (za wyjątkiem "starych" Vector i Hashtable) nie są synchronizowane. Poprawia to efektywność korzystania z kolekcji (synchronizacja jest kosztowna), ale jednocześnie uniemożliwia bezpieczny dostęp do nich przez kilka wątków naraz. Jeśli chcemy udostępnić kolekcje w środowisku wielowątkowym, powinniśmy utworzyć ich synchronizowane widoki. Służą temu metody: public static Collection synchronizedcollection(collection c); public static Set synchronizedset(set s); public static List synchronizedlist(list list); public static Map synchronizedmap(map m); public static SortedSet synchronizedsortedset(sortedset s); public static SortedMap synchronizedsortedmap(sortedmap m); Podkreślmy raz jeszcze, że gdy mowa o widokach (lub "opakowaniach") kolekcji, to tak naprawdę kolekcja jest jedna (nie jest tworzony nowy obiekt, nie są kopiowane wartości elementów). Wszystkie operacje wykonywane są na tej jednej kolekcji.

33 Kolekcje zasady programowania Jak już wiemy, mamy np. dwie implementacje dla listy i po trzy implementacje dla zbioru i mapy. Wybór implementacji zależy od potrzeb naszego programu. Jeśli nasz program tworzy konkretne obiekty klas kolekcyjnych, to zawsze musimy dokonać takiego wyboru. Ale ważną rzeczą jest, by wszelkie inne działania na kolekcjach wykonywać w kategoriach interfejsów, a nie konkretnych klas. Ponadto należy używać kolekcji sparametryzowanych, a nie surowych typów (List czy ArrayList). Przykład. Zdefiniujmy klasę ListUtils, która dostarcza statycznych metod tworzenia dowolnej listy z elementów tablicy (metoda create), dodawania do końca istniejącej listy elementów tablicy (append) oraz wypisywania na konsoli elementów listy (show). Metoda append: Użycie metody sparametryzowanej typem T pozwoli nam uniknąć sytuacji, w której np. do listy osób przypadkowo dodamy liczbę, a także zaoszczędzi nam kodowania, bo zapewnione będą automatyczne konwersje zawężające do konkretnego typu przy sięganiu po elmenty listy. Przypomnijmy, że wobec każdej kolekcji możemy użyć metody add, w szczególności taką metodę zawiera również interfejs List. Ponadto podając w sposób ogólny typ parametru metody append może ona dodawać elementy tablicy do dowolnej listy elementów podanego typu T:

Kolekcje zasady programowania class ListUtils { static <T> void append(list<t> list, T[] items) { for (T item : items) list.add(item); //... Metoda show: Tutaj również powinniśmy podać jako typ parametru nazwę interfejsu List, a nie konkretnej klasy (wówczas metoda będzie działać dla tablicowej implementacji listy ( ArrayList) oraz dla list z dowiązaniami (LinkedList)). Posłużymy się również uniwersalnym parametrem typu <?>, co oznacza dowolny typ i sprawia, że metoda będzie dobra dla list elementów dowolnego typu. static void show(list<?> list) { for (Object o : list) System.out.println(o); Użycie uniwersalnego argumentu typu ma sens, gdy w metodzie nie zmieniamy listy i jej elementów, w innym przypadku piszemy metodę sparametryzowaną <T> void metoda(list<t> list) albo stosujemy wildcards. 34

Kolekcje zasady programowania Teraz w innej klasie możemy elastycznie i uniwersalnie korzystać z tych metod. Możemy użyć ich na rzecz listy napisów implementowanej jako rozszerzalna tablica: String[] items = { "Kot", "Pies", "Zebra", "Tygrys" ; List<String> list1 = new ArrayList<>(); ListUtils.append(list1, items); ListUtils.show(list1); a jeśli trzeba - dla zmienionej implementacji listy (listy z dowiązaniami - LinkedList) i innego typu elementów: Integer[] arr = {1, 2, 3; List<Integer> list2 = new LinkedList<>(); ListUtils.append(list2, arr); ListUtils.show(list2); Przypomnijmy, że kolekcje mogą zawierać wyłącznie referencje do obiektów; zatem musieliśmy użyć tablicy Integer[], a nie int[], jednak - dzięki autoboxingowi - inicjacja tablicy nie nastręczała kłopotów (automatyczne opakowywanie danych typu int w obiekty klasy Integer). 35

Kolekcje zasady programowania Zwróćmy uwagę, że pisząc: List<String> list1 = new... a nie ArrayList<String> list1 = new... czy LinkedList<Integer> list1 = new... jeśli zdecydujemy się na zmianę implementacji (np. z ArrayList na LinkedList lub odwrotnie), to modyfikacja dotknie tylko jednego miejsca kodu (wywołania konstruktora w wyrażeniu new) i mniejsze będzie prawdopodobieństwo popełnienia błędu. Metoda create static <T> List<T> create(t[] items) { ArrayList<T> list = new ArrayList<>(); append(list, items); return list; natomiast jej wywołanie w innej klasie, np.: List<String> list2 = ListUtils.create(...); 36

Własne implementacje kolekcji 37 Architektura kolekcji jest pomyślana również pod kątem tworzenia nowych własnych rodzajów i implementacji kolekcji. Ułatwieniem jest przy tym możliwość skorzystania z gotowych klas abstrakcyjnych, implementujących wszystkie interfejsy kolekcyjne, co upraszcza tworzenie implementacji, bowiem nie trzeba definiować wszystkich metod interfejsów (część z tych metod została w klasach abstrakcyjnych zdefiniowana, pozostaje nam tylko zdefiniowanie metod abstrakcyjnych). Wszystkie te klasy znajdują się w pakiecie java.util, a ich nazwy zaczynają się słowem Abstract... class AbstractCollection<E> Bezpośrednie podklasy: AbstractList, AbstractQueue, AbstractSet, ArrayDeque, ConcurrentLinkedDeque