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 Rozszerzalna tablica Zbalansowane drzewo Lista wiązana Set HashSet TreeSet Interfejsy List ArrayList LinkedList Map HashMap 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(); while (it.hasnext()) { Krzesło k = (Krzesło) it.next(); System.out.println(k.odczytajNumer()); czy są jeszcze elementy? przywrócenie typu (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);