kolekcji
Transkrypt
kolekcji
Podstawy otwartych języków programowania Przechowywanie danych Wiktor Wandachowicz Sposoby przechowywania danych ● ● ● ● ● Typy podstawowe Pojedyncze obiekty Tablice obiektów i tablice typów podstawowych – jednowymiarowe – wielowymiarowe Kolekcje Inne (serializacja, pliki, zewnętrzne źródła danych) Typy podstawowe ● ● ● ● ● ● ● ● ● ● boolean char byte short int long float double Tablice Napisy (String) ● ● ● ● ● ● ● ● Boolean Character Byte Short Integer Long Float Double Równoważne im obiektowe typy opakowujące (ang. wrapper types) Obiekty ● ● ● ● Do obiektów odwołujemy się przez uchwyty. Uchwyty mają znaczenie dla maszyny wirtualnej, właściwe położenie danych obiektu w pamięci może się zmieniać. Uchwyty obiektów, które są “gubione” w programie i nie są już więcej dostępne są zaznaczane do usunięcia przez mechanizm odśmiecania pamięci (ang. garbage collection). Odśmiecanie pamięci następuje automatycznie. Tablice ● Deklaracje tablic: int[] tablica1; int tablica2[]; // składnia C/C++, nie zalecane ● Każdą tablicę przed użyciem trzeba utworzyć lub zainicjalizować: char[] symbole = new char[5]; String napisy[] = { "Hello", "world", "!!!" }; ● Każda tablica ma stałe pole length, które przechowuje ilość jej elementów. Tablice wielowymiarowe ● Deklaracje: double[][] wekt3D = new double[3][3]; ● Używanie – z uwzględnieniem pola length: for (int i = 0; i < wekt3D.length; i++) { for (int j = 0; j < wekt3D[i].length; j++) { System.out.println("wekt3D[" + i + "]" + "[" + j + "] = " + wekt3D[i][j]); } } Kolekcje ● ● ● ● Cechy kolekcji Przegląd interfejsów i klas kolekcji Przykłady użycia Iteratory Cechy kolekcji Kolekcje w Javie przechowują grupy obiektów, wykorzystując gotowe algorytmy składowania danych • Siła kolekcji leży w tym, że są zamienne – mają taki sam interfejs, a różnią się implementacją • Są dwa podstawowe interfejsy: Collection i Map, inne interfejsy są ich rozszerzeniem • Platforma Java zawiera różne implementacje tych samych interfejsów • Każdą kolekcję obiektów można przejść z użyciem iteratora (uniwersalnie), lub w przewidziany tylko dla niej sposób (specyficznie) Interfejsy kolekcji • Kolekcja (Collection) reprezentuje grupę obiektów (elementów) • Zbiór (Set) jest kolekcją która nie może zawierać powtórzeń elementów • Lista (List) jest kolekcją z ustalonym porządkiem (kolejnością) elementów, może zawierać powtórzenia • Odwzorowanie (Map) przyporządkowuje do danych kluczy wartości, klucze nie mogą się powtarzać Cechy kolekcji (Java 1.4) • Kolekcje znajdują się w pakiecie java.util • Kolekcje przechowują referencje do Object, nie są więc czułe na typ danych (gubią informację o typie) • Przy odczycie elementów trzeba wykonywać rzutowanie do odpowiedniego typu danych • W jednej kolekcji mogą być obiekty różnych klas (ponieważ wszystkie są zgodne z Object) • Używając typów opakowujących (ang. wrapper types) można umieszczać w kolekcjach m.in. liczby i pojedyncze litery • Używając metody size() można odczytać aktualną ilość elementów w kolekcji Użycie kolekcji (bez iteratorów) List zakupy = new ArrayList(); zakupy.add(new Zakup("Mleko")); zakupy.add(new Zakup("Rogalik"); zakupy.add(new Zakup("Kakao")); zakupy.add(new Zakup("Miód")); rozszerzalna tablica for (int i = 0; i < zakupy.size(); i++) { System.out.println(i + ". " + zakupy.get(i)); } Map ceny = new HashMap(); ceny.put("Mleko", new Double(1.69)); ceny.put("Rogalik", new Double(0.45)); odwzorowanie (mapa) System.out.println("Cena rogalika: " + ceny.get("Rogalik")); Object[] produkty = ceny.keySet().toArray(); for (int j = 0; j < produkty.length; j++) { System.out.println( produkty[j]+" : " + ceny.get(produkty[j])); } Klasy kolekcji na platformie Java Implementacje Tablica mieszająca Set HashSet Interfejsy List Map Rozszerzalna tablica Zbalansowane drzewo TreeSet ArrayList HashMap Lista wiązana LinkedList TreeMap Klasy kolekcji (c.d.) • Najprostszą klasą kolekcji jest ArrayList (jest to lista zaimplementowana w postaci tablicy). Zapewnia ona szybki dostęp swobodny do elementów i potrafi automatycznie powiększać swój rozmiar. W starej bibliotece kolekcji jej odpowiednikiem jest Vector. • Większą funkcjonalność posiada klasa LinkedList (jest to lista o szybkim dostępie sekwencyjnym do elementów). Także automatycznie powiększa swój rozmiar. Jest jednak stosunkowo powolna przy dostępie swobodnym. Posiada bardziej uniwersalny zestaw metod (np. addFirst() / addLast()). Przegląd metod kolekcji Wybrane metody interfejsu Collection: – – – – – – dodanie elementów – metoda add() odczyt ilości elementów – metoda size() pobranie elementu – metoda get() ustawienie elementu – opcjonalna metoda set() usunięcie elementu – opcjonalna metoda remove() zwrócenie tablicy z elementami kolekcji – metoda toArray() Wybrane metody interfejsu Map: – – – – – dołączenie elementu do klucza – metoda put() odczytanie elementu spod klucza – metoda get() odczyt zbioru wszystkich kluczy – metoda keySet() test czy klucz jest w kolekcji – metoda containsKey() test czy element jest w kolekcji – metoda containsValue() Iteratory Iterator jest obiektem umożliwiającym przemieszczanie się po elementach kolekcji, niezależnie od jej struktury • Obiekt iteratora dla kolekcji uzyskuje się wywołując jej metodę iterator(). Jest on gotów do zwrócenia pierwszego elementu ciągu. • Dla uzyskania następnego elementu kolekcji należy wywołać metodę next() iteratora. • Można sprawdzić, czy są jeszcze jakieś elementy w ciągu wywołując metodę hasNext() • Można usunąć ostatni zwrócony przez iterator element, stosując metodę remove() Przykład użycia iteratora class Krzeslo { /* ... */ } Kod działa równie dobrze, jeśli jest: List sala = new LinkedList(); List sala = new ArrayList(); for (int i = 1; i <= 20; i++) { sala.add(new Krzesło("nr. " + i)); } odczyt iteratora Iterator it = sala.iterator(); czy są jeszcze elementy? while (it.hasNext()) { Krzesło k = (Krzesło) it.next(); przywrócenie typu System.out.println(k.odczytajNumer()); } (rzutowanie) • Użycie iteratorów pozwala korzystać z kolekcji w taki sposób, że łatwo wymienić jej implementację na inną Dwa sposoby iterowania // // // // // // z użyciem pętli while – problemy przy cut & paste 1) Nie można mieć dwóch zmiennych w tym samym zakresie o takiej samej nazwie 2) Zmiana nazwy iteratora – w trzech miejscach 3) Iterator wyczerpany lub konieczność jego reinicjalizacji Iterator it = sala.iterator(); while (it.hasNext()) { Krzesło k = (Krzesło) it.next(); System.out.println(k.odczytajNumer()); } // z użyciem pętli for – bez problemów for (Iterator it = sala.iterator(); it.hasNext(); ) { Krzesło k = (Krzesło) it.next(); System.out.println(k.odczytajNumer()); } Kolekcje w Javie 5 i 6 ● ● ● ● W najnowszej wersji Javy zostały dodane rozszerzenia języka (m.in. szablony, rozszerzona pętla for), dzięki którym użycie kolekcji jest łatwiejsze i mniej podatne na błędy. Kolekcje zyskały możliwość przechowywania obiektów ściśle określonego typu. Iteracja po wszystkich obiektach kolekcji jest o wiele krótsza w zapisie (brak rzutowań, mniejsza ilość błędów). Możliwa jest także iteracja po elementach tablic korzystając ze składni rozszerzonej pętli for. Kolekcje w Javie 5 i 6 (c.d.) ● ● ● Typy uogólnione (ang. generic types) zostały wprowadzone w Javie zwłaszcza po to, aby już na etapie kompilacji pozbyć się problemu dodawania do kolekcji różnego rodzaju danych. Kolekcje są zgodne binarnie w dół, można więc m.in. korzystać w skompilowanym kodzie (biblioteki) z nowych kolekcji. Odbywa się to przez mechanizm wymazywania (ang. erasure) – „magię kompilatora”. Cechy kolekcji (Java 5 i 6) • Kolekcje znajdują się w pakiecie java.util • Kolekcje przechowują referencje do wskazanego typu danych, przy użyciu typów uogólnionych • W jednej kolekcji mogą być obiekty jedynie wskazanej klasy oraz jej klas pochodnych • Przy odczycie elementów odtwarzany jest odpowiedni typ danych (nie jest potrzebne rzutowanie) • Liczby i pojedyncze litery można umieszczać wprost w kolekcjach przez użycie mechanizmu autoboxing • Używając metody size() można odczytać aktualną ilość elementów w kolekcji Kolekcje w nowym stylu public class Klasa { private String napis; public Klasa(String parametr) { this.napis = parametr; } public String toString() { return "Klasa(" + napis + ")"; } } // kolekcja uwzględniająca typ danych List<Klasa> dane = new LinkedList<Klasa>(); for (int i = 1; i <= 7; i++) { dane.add(new Klasa("obiekt_" + i)); } // iterator uwzględniający typ danych Iterator<Klasa> it = dane.iterator(); while (it.hasNext()) { Klasa obiekt = it.next(); // nie ma rzutowania! System.out.println(obiekt); } Trzy sposoby iterowania // z użyciem pętli while – problemy przy cut & paste Iterator<Klasa> it = dane.iterator(); while (it.hasNext()) { Klasa obiekt = it.next(); System.out.println(obiekt); } // z użyciem pętli for – bez problemów for (Iterator<Klasa> it = dane.iterator(); it.hasNext(); ) { Klasa obiekt = it.next(); System.out.println(obiekt); } // z użyciem rozszerzonej pętli for // (przykład równoważny poprzedniej pętli) for (Klasa obiekt : dane) { System.out.println(obiekt); } Stare kolekcje w nowym wydaniu // RÓŻNE TYPY DANYCH W JEDNEJ KOLEKCJI // Kolekcja przechowująca elementy typu Object List tradycyjna = new Vector(); // operacja poprawna tradycyjna.add(new Integer(5)); // autoboxing tradycyjna.add(6); // Poniższy kod kompiluje się, ale pojawia się błąd // w trakcie działania programu /* tradycyjna.add(new Klasa("obiekt")); */ // Nowy sposób iterowania po starej kolekcji for (Object o : tradycyjna) { // unboxing: Object -> Integer -> int int x = (Integer) o; System.out.println(o.getClass().getName() + " | " + x); } Odwzorowania (mapy) w nowym stylu // AUTOBOXING I UNBOXING W PRAKTYCE // odwzorowanie uwzględniające wskazany typ klucza i wartości Map<String,Double> ceny = new HashMap<String,Double>(); ceny.put("Mleko", new Double(1.69)); // autoboxing: automatyczna konwersja double -> Double ceny.put("Rogalik", 0.45); System.out.println("Ceny produktów:"); for (String produkt : ceny.keySet()) { System.out.println(produkt + "\t\t" + ceny.get(produkt)); } System.out.print("Zakup po jednej sztuce to wydatek: "); double suma = 0.0; // też dobrze dla: Double suma = 0.0; for (String produkt : ceny.keySet()) { // unboxing Double -> double przed sumowaniem suma += ceny.get(produkt); } System.out.println(suma);