Kolekcje, wyjątki, strumienie

advertisement
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 (c.d.)
• 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
• Kolekcje znajdują się w pakiecie java.util
Użycie kolekcji
List zakupy = new ArrayList();
zakupy.add("Mleko");
zakupy.add("Rogalik");
zakupy.add("Kakao");
zakupy.add("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.49));
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 Krzeslo("nr. " + i));
}
odczyt iteratora
Iterator it = sala.iterator();
czy są jeszcze elementy?
while (it.hasNext()) {
Krzeslo k = (Krzeslo) it.next();
System.out.println(k.odczytajNumer());
przywrócenie typu
}
(rzutowanie)
• Użycie iteratorów pozwala korzystać z kolekcji w taki
sposób, że łatwo wymienić jej implementację na inną
Wyjątki
Podstawą filozofii Javy jest założenie, że:
źle sformułowany kod nie zostanie wykonany
• Sytuacja błędna powoduje utworzenie nowego obiektu
wyjątku i rzucenie go
• Kod, który wyrzuca wyjątki można spróbować wykonać,
a pojawiające się wyjątki można łapać
• Złapany wyjątek można obsłużyć albo rzucić ponownie
• Jeśli kod w metodzie rzuca wyjątek (nie jest on obsłużony),
musi być to wyspecyfikowane w deklaracji metody
• Wszystkie wyjątki pochodzą od klasy java.lang.Exception
Łapanie wyjątków
• Odbywa się z użyciem bloków try i catch.
– Pierwszy blok (try – spróbuj) obejmuje kod, który może
spowodować wyjątek.
– Drugi blok (catch – złap) określa jakie wyjątki chce obsłużyć i
zawiera kod z obsługą błędów.
• Jeśli wyjątek został złapany, można m.in.
– wyświetlić informację o pochodzeniu błędu (jest to najczęściej
używane): e.printStackTrace()
– odczytać komunikat błędu: e.getMessage()
– odczytać źródło błędu: e.getCause()
Łapanie wyjątków – przykład (1)
import java.io.*;
public class Imie {
static void odczytajImie() {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
try {
System.out.print("Podaj imie: ");
String imie = in.readLine();
// może być wyjątek
blok try
System.out.print("Witaj " + imie + "!");
} catch (IOException e) {
e.printStackTrace();
lokalna obsługa wyjątku
}
}
public static void main(String[] args) {
odczytajImie();
}
}
Łapanie wyjątków – przykład (2)
import java.io.*;
specyfikacja wyjątku
public class Imie {
static void odczytajImie() throws IOException {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
System.out.print("Podaj imie: ");
String imie = in.readLine();
// może być wyjątek
System.out.print("Witaj " + imie + "!");
}
public static void main(String[] args) {
try {
odczytajImie();
blok try
} catch (IOException e) {
e.printStackTrace();
obsługa wyjątku
}
}
}
Rzucanie wyjątków
• Aby zasygnalizować błąd, należy utworzyć obiekt
wyjątku (podając przyczynę błędu)
• Następnie należy rzucić wyjątek, z użyciem słowa
kluczowego throw:
long suma = 0;
int iloscNieujemnych = 0;
void dodajNieujemna(int liczba)
throws IllegalArgumentException {
if (liczba > 0) {
suma += liczba;
iloscNieujemnych++;
} else
throw new IllegalArgumentException("liczba <= 0");
}
Tworzenie własnych klas wyjątków
• Nowa klasa wyjątku powinna dziedziczyć z Exception
• Powinna deklarować przynajmniej dwa konstruktory:
• bezparametrowy
• z jednym argumentem typu String
• Może zawierać dodatkowe pola i metody
public class AccessDeniedException extends Exception {
public AccessDeniedException() {
super();
}
public AccessDeniedException(String s) {
super(s);
}
}
Strumienie wejścia/wyjścia
•
•
•
•
Typy strumieni
Łączenie strumieni dla zwiększenia możliwości
Różne źródła i ujścia danych
Przykłady użycia
Strumienie danych
Strumień jest abstrakcyjną reprezentacją
dowolnego źródła lub ujścia danych
• Na podstawie dwóch logicznych operacji wyróżniamy
dwa typy strumieni:
– strumienie wejściowe (odczyt danych)
– strumienie wyjściowe (zapis danych)
• Strumień jest zawsze stowarzyszony z jakimś
źródłem danych (odczyt) lub ujściem (zapis)
• Wyróżniamy także strumienie bajtowe i znakowe
• W Javie obsługę strumieni zapewnia pakiet java.io
Strumienie wejściowe i wyjściowe
strumień wejściowy
źródło
danych
informacja
odczyt
Program
strumień wyjściowy
Program
zapis
informacja
ujście
danych
Algorytmy odczytu i zapisu
Odczyt
Zapis
otwórz strumień
jeśli jest jeszcze informacja
odczytaj informację
zamknij strumień
otwórz strumień
jeśli jest jeszcze informacja
zapisz informację
zamknij strumień
Hierarchia strumieni
Strumienie
bajtowe
Strumienie
znakowe
• Klasy strumieni są w pakiecie java.io
• Przy nieudanych operacjach pojawiają się
wyjątki klasy java.io.IOException
• Strumienie bajtowe i znakowe zapewniają
praktycznie taką samą funkcjonalność
Strumienie bajtowe
Dwa najbardziej ogólne strumienie bajtowe to:
• InputStream – do odczytu, oraz
• OutputStream – do zapisu ciągów bajtów.
InputStream
OutputStream
abstract int read()
abstract void write(int b)
int read(byte[] b)
void write(byte[] b)
int read(
byte[] b, int off, int len)
void write(
byte[] b, int off, int len)
long skip(long n)
void flush()
void close()
void close()
Strumienie znakowe
Podstawowymi strumieniami znakowymi są:
• Reader – do odczytu, oraz
• Writer – do zapisu ciągów znaków.
Reader
Writer
int read()
void write(int c)
int read(char[] cbuf)
void write(char[] cbuf)
abstract int read(char[]
cbuf, int off, int len)
abstract void write(char[]
cbuf, int off, int len)
long skip(long n)
void flush()
void close()
void close()
Rozszerzanie funkcjonalności
• Zwiększenie funkcjonalności odbywa się przez użycie
wyspecjalizowanych strumieni. Korzystają one
z innych strumieni, aby wykonywać swoją pracę.
• Robi się to przez opakowanie jednego typu
strumienia w inny (aby to zadziałało musi istnieć
w wybranej klasie odpowiedni konstruktor).
InputStream
InputStreamReader
BufferedReader
int read(byte[] b)
int read(char[] cbuf)
String readLine()
Odczyt standardowego wejścia
import java.io.*;
public class PowielajLinie {
public static void main(String[] args)
throws IOException {
System.out.println("Wpisuj linie tekstu. " +
"Koniec gdy pusta linia.");
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
InputStreamReader(System.in)
System.in
String linia;
do {
linia = in.readLine();
System.out.println(linia);
} while (linia.length() > 0);
}
}
InputStream
Strumienie wyspecjalizowane
Zamiana strumieni bajtowych na znakowe:
• InputStreamReader / OutputStreamWriter
Odczyt i zapis z uwzględnieniem znaków końca linii:
• BufferedReader – readLine()
• BufferedWriter – newLine()
Strumień z buforem (większa wydajność):
• BufferedInputStream / BufferedOutputStream
Przeciążone metody println():
• PrintStream / PrintWriter
Strumienie wyspecjalizowane
Odczyt i zapis tablic bajtów lub znaków (w pamięci):
• ByteArrayInputStream / ByteArrayOutputStream
• CharArrayReader / CharArrayWriter
Odczyt i zapis łańcuchów napisów (w pamięci):
• StringReader / StringWriter
Odczyt i zapis plików:
• FileInputStream / FileOutputStream
• FileReader / FileWriter
Serializacja obiektów:
• ObjectInputStream / ObjectOutputStream
Odczyt i zapis plików
• Żeby otworzyć plik, wystarczy stworzyć obiekt
wybranej klasy, podając nazwę pliku w postaci
łańcucha znaków lub obiektu klasy File
 FileInputStream – odczyt pliku binarnego
 FileOutputStream – zapis pliku binarnego (*)
 FileReader – odczyt pliku znakowego
 FileWriter – zapis pliku znakowego (*)
(*) możliwe otwarcie pliku do dopisywania, bez usuwania
poprzedniej zawartości
• Otwarty strumień można opakować w wybrany
strumień wyspecjalizowany
• Po zakończeniu strumień należy zamknąć – close()
Klasa File
• NIE JEST to plik – służy raczej jako OPIS ŚCIEŻEK do
plików lub katalogów:
File plik = new File("plik.txt");
FileReader in = new FileReader(plik);
• Przy użyciu tej klasy można także m.in.
– sprawdzać informacje o pliku: isDirectory(),
isFile(), canRead(), canWrite(), exists(),
length(), lastModified()
– wykonywać operacje na plikach i katalogach:
(delete(), mkdir(), mkdirs(), renameTo()
– odczytywać zawartość katalogu: list(), listFiles()
Download