Kolekcje
Kolekcje w języku Java (List, Set, Map, Queue)Kolekcje
- wszędzie gdzie występuje
Linkedzachowywana jest kolejność wstawiania - dostęp do pierwszego i ostatniego elementu listy zawsze odbywa się w stałym czasie, O (1) ponieważ linki są trwale przechowywane w pierwszym i ostatnim elemencie, więc dodanie pozycji na koniec listy nie oznacza, że musisz iterować całą listę w poszukiwaniu ostatniego elementu. Jednak dostęp do elementu bądź ustawienie elementu za pomocą jego indeksu wymaga czasu O (n) w przypadku
LinkedList - w ogólnym przypadku
LinkedListtraci doArrayListw zużyciu pamięci i szybkości operacji. Ale to zależy od problemu, który próbujesz rozwiązać.
Immutable lists
Najprostszym sposobem utworzenia listy jest wywołanie metody of interfejsu List. Przed wersją Java 9 innym sposobem tworzenia niemodyfikowalnych list było użycie Arrays.asList(...).
List<String> emptyList = List.of(); // 0 elements
List<String> names = List.of("Larry", "Kenny", "Sabrina"); // 3 elements
List<Integer> numbers = List.of(0, 1, 1, 2, 3, 5, 8, 13); // 8 elements
Zwraca immutable list zawierającą wszystkie przekazane elementy lub pustą listę. Użycie tej metody jest wygodne podczas tworzenia stałych list lub testowania kodu. Wykonajmy kilka operacji:
List<String> daysOfWeek = List.of( "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" );
System.out.println(daysOfWeek.size()); // 7 System.out.println(daysOfWeek.get(1)); // Tuesday
System.out.println(daysOfWeek.indexOf("Sunday")); // 6
List<String> weekDays = daysOfWeek.subList(0, 5); System.out.println(weekDays); // [Monday, Tuesday, Wednesday, Thursday, Friday]
Ponieważ jest immutable, będą działać tylko metody, które nie zmieniają elementów na liście. Inne rzucą wyjątek.
daysOfWeek.set(0, "Funday"); // throws UnsupportedOperationException
daysOfWeek.add("Holiday"); // throws UnsupportedOperationException
Mutable lists
Kiedy musisz użyć listy mutowalnej, możesz wziąć jedną z dwóch powszechnie używanych implementacji mutowalnych interfejsu List.
List<Integer> numbers = new ArrayList<>();
numbers.add(15);
numbers.add(10);
numbers.add(20);
System.out.println(numbers); // [15, 10, 20]
numbers.set(0, 30); // no exceptions here
System.out.println(numbers); // [30, 10, 20]
Jeśli masz immutable list, możesz pobrać z niej mutowalną wersję, używając następującego kodu:
List<String> immutableList = Arrays.asList("one", "two", "three");
List<String> mutableList = new ArrayList<>(immutableList);
Array
Tablica jest jednym z typów kolekcji.
public class Array2D { public static void main(String[] args) {
String[][] salutation = {
//[?][0] [?][1] [?][2] {"Mr. ", "Mrs. ", "Ms. "},
//[0][?] {"Kumar", "White", "Rose"}
//[1][?] };
//Mr. Kumar:
System.out.println(salutation[0][0] + salutation[1][0]);
//Mrs. White:
System.out.println(salutation[0][1] + salutation[1][1]);
//Ms. Rose:
System.out.println(salutation[0][2] + salutation[1][2]);
}
}
List
- zachowuje kolejność elementów
- powiększa się wraz z dodawaniem elementów
- ten sam obiekt może być przechowywany wiele razy
- można wstawić wartość
nullwiele razy - limit pojemności liczby elementów to 32 bity (2 147 483 648), ponieważ int jest indeksem - metoda
size()zwraca int.
Przykładowe implementacje:
java.util.ArrayList– najczęstszy wybór z racji najbardziej uniwersalnego zastosowania. Inne implementacje mają przewagę tylko w bardzo specyficznych przypadkach. Jeśli nie wiesz, jakiej listy potrzebujesz, wybierz tą. Preferowana, gdy częściej szukamy i odwołujemy się do obiektówjava.util.LinkedList– szybsze dodawanie/usuwanie; może być traktowana jako List lub Queue, ponieważ implementuje oba te interfejsy.LinkedListma przewagę w przypadku dodawania elementów pojedynczo, w dużej ilości, w sposób trudny do przewidzenia wcześniej, kiedy przejmujemy się ilością zajmowanej pamięci
Niektóre metody:
add(E e)– dodaje element na koniec listyadd(E e, int index)– dodaje element na wybraną pozycję (nie usuwa elementu już tam się znajdującego)remove(int index)– usuwa element pod wskazanym indeksemremove(E e)– usuwa pierwsze wystąpienie wskazanego elementu (obiektu)set(E e, int index)– wstawia element na wybraną pozycję (tym samym poprzednio znajdujący się element zostaje usunięty)get(int index)– zwraca element o wybranym indeksieaddAll(Collection)– dodaje wszystkie elementy z jednej kolekcji do drugiej i wstawia je na konieccontains(E e)– zwracatruejeżeli lista zawiera dany elementisEmpty()– zwracatruejeśli lista jest pustasize()– zwraca aktualny rozmiar listyindexOf(E e)– wyszukuje element na liście i zwraca pierwszy indeks wystąpienialastIndexOf(E e)– j.w. ale zwraca ostatnie wystąpienie
Przykład:
List<String> komputery = new LinkedList<>();
komputery.add("ZX Spectrum");
komputery.add("Commodore 64");
List<String> ulubione = new LinkedList<>();
ulubione.add("Commodore 64");
ulubione.addAll(komputery);
System.out.println(ulubione.get(1));
System.out.println(ulubione.indexOf("Commodore 64"));
Set
- kolejność nie ma znaczenia (nie pozwala na bezpośredni dostęp do obiektu poprzez podanie np. indeksu)
- przechowuje elementy unikalne - ten sam obiekt może znajdować się w zbiorze tylko raz (nie pozwala przechowywać duplikatów)
- można wstawić wartość
nulljeden raz - element młodszy nadpisuje element starszy
- aby dostać się do obiektu musimy skorzystać z pętli
foreachlub specjalnego typuIterator
Przykładowe implementacje:
java.util.HashSet– najczęściej występująca – dobra wydajnościowo; brak zachowania kolejnościjava.util.TreeSet– umieszcza nowe elementy kolekcji poprzez użycieComparatora; wszystkie elementy od razu posortowane i zachowują swoją kolejność. Implementacja oparta o drzewa czerwono czarne, która oprócz unikalności elementów gwarantuje nam uporządkowanie wstawianych elementów zgodnie z naturalnym porządkiem. Porządek ten wyznaczany jest poprzez implementację interfejsuComparablelub zastosowanie odpowiedniegoComparatora(oznacza to, że elementy w zbiorze muszą implementować interfejsComparablelub trzeba dostarczyćComparator).java.util.LinkedHashSet– zachowuje kolejność wpisywanych elementów (jak w Liście)
Niektóre metody:
add(E e)– dodawanie elementuaddAll(Collection)– dodaje wszystkie elementy z jednej kolekcji do drugiejremove(E e)– usuwa wybrany element po wartości, ponieważSetnie ma indeksówisEmpty()– zwracatruejeśli zbiór jest pustysize()– zwraca aktualny rozmiar zbioruiterator()– zwraca obiekt typuIteratorumożliwiający iterowanie zbioru- Ze względu na brak zachowania kolejności zbiór nie posiada metod takich jak
get,indexOf,lastIndexOf. Zamiast tego posiada metody:contains(E e)– zwracatruejeśli element znajduje się w zbiorzecontainsAll– sprawdza czy zbiór zawiera wszystkie elementy zbioru
Przykład HashSet
Set<String> komputery = new HashSet<>();
komputery.add("ZX Spectrum");
komputery.add("Commodore 64");
komputery.add("Commodore 64");
komputery.add("Amiga 500");
System.out.println(komputery.size());
System.out.println(komputery.contains("Commodore 64"));
komputery.remove("Commodore 64");
System.out.println(komputery.contains("Commodore 64"));
Przykład TreeSet:
//Tworzymy zmienną o typie interfejsu i inicjalizujemy:
Set<String> imiona = new TreeSet<>();
imiona.add("Zosia");
imiona.add("Krysia");
imiona.add("Albert");
imiona.add("Krysia");
for (String imie: imiona) {
System.out.println(imie);
}
Map
- mapy nie implementują interfejsu Collection
- przechowuje pary klucz: wartość
- w odróżnieniu od indeksu w liście (gdzie wymaga się liczb całkowitych), kluczem może być inny obiekt niemodyfikowalny
- klucz musi być unikalny
- wartości mogą się powtarzać
Przykładowe implementacje:
java.util.HashMap– najczęściej występująca – dobra wydajnościowo; brak zachowania kolejnościjava.util.LinkedHashMap– zachowuje kolejność wpisywanych elementów (jak w Liście)java.util.TreeMap– umieszcza nowe elementy kolekcji poprzez użycieComparatora; wszystkie elementy od razu posortowane względem klucza i zachowują swoją kolejność
Niektóre metody:
put(Key key, Value value)– umieszcza nową parę klucz: wartość w mapie (lub nadpisanie, gdy klucz istnieje!)putAll– dodaje wszystkie elementy z jednej mapy do drugiejcontainsKey(Object o)– zwracatruejeśli mapa zawiera wskazany kluczcontainsValue(Object o)– zwracatruejeśli mapa zawiera wskazaną wartośćisEmpty()– zwracatruejeśli mapa jest pustasize()– zwraca aktualny rozmiar mapyremove(Object key)– usuwa wybraną parę klucz : wartość na podstawie kluczaget(Object key)– zwraca wartość przypisaną dla wskazanego kluczakeySet()– zwraca set wszystkich kluczy mapiereplace(Key key, Value value)– podmienia wartość dla wskazanego klucza
Przykład HashMap z wykorzystaniem pętli foreach do iterowania:
Map<String, String> procesory = new HashMap<>();
procesory.put("IBM PC", "Intel x86");
procesory.put("Atari ST", "Motorola 68000");
procesory.put("Commodore 64", "MOS 6502");
procesory.put("Amiga 500", "Motorola 68000");
procesory.put("IBM PC", "Intel x86-64");
System.out.println(procesory.size());
System.out.println(procesory.get("Amiga 500"));
System.out.println(procesory.get("IBM PC"));
//iterowanie po wartościach:
for (String wartosc: procesory.values()) {
System.out.println(wartosc);
}
//iterowanie po kluczach:
for (String klucz: procesory.keySet()) {
System.out.println(klucz);
System.out.println(procesory.get(klucz));
}
//iterowanie po parach:
for (java.util.Map.Entry para: procesory.entrySet()) {
System.out.println(para.getKey()); System.out.println(para.getValue());
}
Queue
Pozwala na implementację kolejek typu FIFO (first-in-first-out) i FILO (first-in-last-out).
Przykładowe implementacje:
java.util.ArrayDeque— kolejka oparta o tablice, pozwala na dostęp zarówno od strony głowygetFirst()jak i ogonagetLast(). Elementy przechowywane są w kolejności dodawaniajava.util.PriorityQueue– pozwala na przechowywanie i dostęp do elementów wg określonego kryterium (komparatora), sortując elementy wg niego w momencie dodania do kolejki. Przydatna w sytuacjach gdy mamy np. kolejkę obiektów do przetworzenia i chcemy zawsze obsłużyć ważniejsze szybciej niż te mniej ważne
Niektóre metody:
boolean offer(E e)wstawia określony element do kolejki, jeśli jest to możliwe od razu bez naruszania ograniczeń pojemności; zwracatrue/falsew zależności od wyniku tej operacjiE remove()pobiera i usuwa head tej kolejki; jeśli jest pusty, metoda rzucaNoSuchElementExceptionE poll()pobiera i usuwa head tej kolejki lub zwracanulljeśli ta kolejka jest pustaE element()pobiera, ale nie usuwa head kolejki; jeśli jest pusty, metoda rzucaNoSuchElementExceptionE peek()pobiera, ale nie usuwa head tej kolejki lub zwracanulljeśli ta kolejka jest pustaadd(E element)metoda działa tak samo jakoffer(E e)ale rzucaIllegalStateExceptionjeśli nie jest obecnie dostępna przestrzeń;
Przykład
Queue<String> kolejka = new ArrayDeque<String>();
kolejka.add("pierwszy");
kolejka.add("drugi");
System.out.println(kolejka.remove()); //wypisze "pierwszy"