#Niezbędnik Juniora. Kolekcje w języku Java

By 16 czerwca 2015Niezbędnik Juniora

W kursie Javy (a dokładniej w lekcji 5) omówione były już podstawy kolekcji w języku Java. Dzisiaj poszerzamy tą wiedzę o informacje, które moga Ci się przydać w pracy oraz na rozmowach.Kolekcje to ta grupa obiektów z którą spotkasz się bardzo szybko w swojej karierze. O ile często wystarczy ich podstawowa znajomość, warto nieco się zgłębić i poznać ich wady/zalety, aby wiedzieć którą kolekcje zastosować kiedy. W tej lekcji zajmiemy się implementacjami, które rozszerzają interfejs Collection.

Słowem wstępu – notacja dużego O

Zanim przejdziemy do opisu poszczególnych kolekcji, zatrzymajmy się na moment, żeby omówić sobie notację dużego O, która będzie nam potrzebna do porównania różnych kolekcji. Szczegółowe informacje na ten temat zawrzemy w oddzielnym wpisie, dzisiaj jedynie wstęp.

Notacja dużego O służy w informatyce do porównywania klas algorytmów w sposób miarodajny – weźmy na przykład sortowanie. Najprostszym sposobem na porównanie różnych podejść jest zmierzenie czasu sortowania tych samych elementów, np. 100 liczb. Metoda A zrobi to w 10 jednostek czasu a metoda B zrobi to w 20 jednostek czasu. Czy znaczy to, że metoda A jest lepsza / wydajniejsza? Absolutnie nie! Wykonując próbę dla 1000 liczb okaże się, że metoda A potrzebuje już 100 jednostek czasu, a metoda B tylko 50. Aby rozwiązać ten problem wprowadzono notację dużego o, która mówi nam nie ile czasu zajmie pewna operacja, ale jak ten czas się zmienia w zależności od ilości danych, na których pracujemy.

Brzmi skomplikowanie, ale to naprawdę proste! Weźmy dla przykładu algorytm, który losuje liczbę z przedziału (a, b). Dla uproszczenia pomyślmy o tym jako o człowieku, który z worka wszystkich możliwych liczb z tego przedziału wybiera jedną. Czy czas potrzebny na wybranie tej liczby zmieni się w zależności od tego czy w worku będzie 5, 50 czy 500000 liczb? Nie! Oznacza to, że algorytm ten działa w czasie stałym, który oznaczamy jako O(1). A dlaczego O(1) a nie np. O(3) ? Ponieważ interesuje nas jedynie sposób, w jaki ten czas rośnie, a nie sam czas. Jeśli jest stały, używamy po prostu jednostki, aby pokazać, że się nie zmienia.

Jako drugi przykład weźmy algorytm, który dla każdej liczby od zera do x wypisuje ją na konsoli. W zależności od wejścia (x w tym wypadku) nasz algorytm będzie musiał wykonać różną ilość operacji – w tym wypadku dla każdej jednej liczby będzie to określona, stała liczba operacji (ponownie, nie ma znaczenia, czy będą to 3 operacje czy 50, ważne, że jest to stała ilość). Dlatego algorytm ten ma złożoność obliczeniową O(n).

A co, jeśli poprzedni algorytm zmodyfikujemy tak, aby wypisywał też kwadrat tej liczby? Nic się nie zmieni, ponieważ wypisanie oraz obliczenie kwadrata liczby są operacjami o stałym czasie wykonania, a więc złożoność nadal będzie wynosiła O(n).

Złożoność jest bardzo ważna ponieważ często nie wiemy, ile dokładnie danych będzie przetwarzanych w naszym systemie i przez to nie jesteśmy w stanie wykonać miarodajnych prób czasowych. Nawet jesli byśmy to zrobili, te założenia będą się zmieniać z czasem. Dlatego o ile nie jesteśmy absolutnie pewni, że będziemy przetwarzać określoną, niewielką ilość informacji (np. kilkanaście) w danym miejscu, zawsze powinniśmy wybierać algorytm o najmniejszej złożoności, a nie taki, który działa szybciej (czasowo) podczas testów. Oczywiście od tej reguły można robić wyjątki, ale tylko pod warunkiem, ze naprawdę wiemy co robimy, a decyzja ta została zweryfikowana i udokumentowana w specyfikacji projektu.

Porównanie złożoności operacji we wszystkich kolekcjach możesz znaleźć na stronie http://infotechgems.blogspot.ie/2011/11/java-collections-performance-time.html .

Zachęcamy także do zapoznania się z materiałami dostępnymi pod adresem https://codility.com/media/train/1-TimeComplexity.pdf .

Rodzaje kolekcji w Javie

Poniżej omówimy poszczególne rodzaje kolekcji, dla każdej z nich podamy zastosowania, najważniejsze cechy oraz standardowe implementacje.

Tablica

Dla przekory pierwszą omawianą strukturą będzie tablica. Dla przekory ponieważ tablice nie są stricte kolekcjami (w tym sensie, że nie implementują interfejsu Collection), a elementem języka Java. Jako jedyne mogą przechowywać prymitywy (we wszystkich innych przypadkach ma miejsce autoboxing – automatyczna zamiana prymiwtywu na odpowiadającą mu reprezentacje obiektową – jeśli spróbujemy dodać prymityw).

Tablica to uporządkowany (mający kolejność, ale niekoniecznie posortowany!) zbiór elementów, które fizycznie w pamięci komputera są umieszczone obok siebie. Umożliwiają dostęp losowy (tzw. Random Access), czyli dostęp do elementu na dowolnie wybranej pozycji. Elementy tablicy mogą mieć wartość null! Aby utworzyć tablicę stosujemy poniższą konstrukcję:

String[] tablica = new String[6];

Dozwolone jest także umieszczenie nawiasów kwadratowych za nazwą zmiennej:

String tablica[] = new String[6];

Jak pewnie zauważyłaś, nie używamy tutaj nawiasów () jak w przypadku konstruktorów obiektów.

Inna dopuszczalna składnia, która od razu inicjuje tablicę wartościami jest poniższa:

String[] tablica = {"jeden", "dwa", "trzy", "cztery", "pięć", "sześć"};

Istnieje jeszcze jedna możliwość zadeklarowania tablicy – jako ostatni argument metody, dzięki czemu może one przyjmować nieokreśloną liczbę argumentów. Weźmy na przykład poniższą metodę:

public void wypiszStringiWNowychLiniach(String... tablica) { //argument ten jest tablicą
    for (i = 0; i<tablica.length; i++) {
        System.out.println(tablica[i]);
    }
}

//przykładowe wywołania
wypiszStringiWNowychLiniach("jeden", "dwa", "trzy", "cztery", "pięć");
String[] tablica = {"jeden", "dwa", "trzy", "cztery", "pięć"};
wypiszStringiWNowychLiniach(tablica);

W powyższym przykładzie widzimy też jak odwoływać się do elementów tablicy – w przeciwieństwie do kolekcji nie używamy metod, a składni, pisząc nazwę zmiennej a następnie w nawiasie kwadratowym numer elementu.

Ostatnia bardzo ważna informacja na temat tablic jest taka, że rozmiaru tablicy nie można zmieniać! Jeśli utworzymy tablicę z 6 ‚miejscami’, aby  dodać do niej 7 element, będziemy musieli utworzyć nową, większą, tablicę i przepisać wcześniejsze 6 elementów.

Lista (java.util.List)

Listy są jednymi z najczęściej używanych kolekcji – koncepcyjnie są podobne do tablic, można jednak (tak jak w przypadku wszystkich pozostałych kolekcji) dynamicznie zmieniać ich rozmiar (dodawać i usuwać elementy). Elementy w liście mogą się powtarzać (tzn. ten sam obiekt może być na liscie wielokrotnie). Istnieje kilka standardowych implementacji, z których najważniejsze zostały opisane poniżej:

Klasa Cechy Zastosowanie
ArrayList
  •  w ‚tle’ przechowuje dane w tablicy, której samodzielnie zmienia rozmiar wg potrzeb
  • dostęp do dowolnego elementu w czasie O(1)
  • dodanie elementu O(1), pesymistyczny przypadek O(n)
  • usunięcie elementu O(n)
 Najczęstszy wybór z racji najbardziej uniwersalnego zastosowania. Inne imeplementacje mają przewagę tylko w bardzo specyficznych przypadkach. Jeśli nie wiesz, jakiej listy potrzebujesz, wybierz tą.
LinkedList
  •  przechowuje elementy jako lista powiązanych ze sobą obiektów (tj. pierwszy element wie, gdzie jest drugi, drugi wie, gdzie trzeci itd)
  • dostęp do dowolnego elementu O(n) (iteracyjnie O(1) )
  • dodanie elementu O(1)
  • usunięcie elementu O(1)
 LinkedList ma 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.W praktyce nie spotkałem się z sytuacją, w której LinkedList byłoby wydajniejsze od ArrayList
Vector
  • Istnieje od początku Javy, z założenia miała to być  obiektowa reprezentacja tablicy
  • Funkcjonalność i cechy analogiczne do ArrayList, ale znacznie mniej wyrafinowane
API oficjalnie zaleca korzystanie z klasy ArrayList zamiast Vector

Implementacją interfejsu List jest także klasa Stack – będziemy jednak omawiać stos jako osobną strukturę i tam omówimy tą klasę.

Aby utworzyć nową listę (do przykładów bedziemy korzystać z ArrayListy) wystarczy wywołać konstruktor:

List<String> lista = new ArrayList<String>();

Do dodawania elementu służy metoda add:

lista.add("jeden");

Pobieranie elementów odbywa się z użyciem metody get, podając pozycję elementu jako argument:

String pierwszy = lista.get(0);

Stan listy możemy sprawdzić korzystając z pomocniczych metod:

int iloscElementow = lista.size();
boolean listaJestPusta = lista.isEmpty();

Set / zbiór (java.util.Set)

Sety to zbiory elementów, w których nie możemy uzyskać dostępu do n’tego elementu, a jedynie możemy pobrac iterator (obiekt, który pozwoli nam pobrać kolejne elementy, w zależności od implementacji w porządku posortowanym lub losowym). Elementy w Setach nie mogą się powtarzać.

Klasa Cechy Zastosowanie
HashSet
  • zbiór nieposortowany
  • kolejność iteracji nieokreślona, może się zmieniać
  • dodanie elementu oraz sprawdzenie czy istnieje ma złożoność O(1)
  • pobranie kolejnego elementu ma złożoność O(n/h), gdzie h to parametr wewnętrzny (pesymistyczny przypadek: O(n) )
Sytuacje, kiedy nie potrzebujemy dostępu do konkretnego elementu, ale potrzebujemy często sprawdzać, czy dany element już istnieje w kolekcji (czyli chcemy zbudować zbiór unikalnych wartości).W praktyce często znajduje zastosowanie do raportowania/zliczeń oraz jako ‚przechowalnia’ obiektów do przetworzenia (jeśli ich kolejność nie ma znaczenia).
LinkedHashSet
  • analogiczne, jak HashSet (dziedziczy po nim)
  • kolejność elementów używając iteratora jest deterministyczna i powtarzalna (zawsze będziemy przechodzić przez elementy w tej samej kolejności)
  • pobranie kolejnego elementu ma złożoność O(1)
Jesto to mniej znana i rzadziej stosowana implementacja, często może ona zastąpić HashSet poza specyficznymi przypadkami. W praktyce do iterowania w określonej kolejności często używane sa inne kolekcje (Listy, kolejki)
TreeSet
  • Przechowuje elementy posortowane wg porządku naturalnego (jeśli implementują one interjfejs Comparable, w przeciwnym wypadku porządek jest nieokreslony)
  • Wszystkie operacje (dodanie, sprawdzenie czy istnieje oraz pobranie kolejnego elementu) mają złożoność O(log n)
Jeśli potrzebujemy, aby nasz zbiór był posortowany bez dodatkowych operacji (sortowanie nastepuje już w momencie dodania elementu) oraz iterować po posortowanej kolekcji.

Aby utworzyć nowy set (do przykładów bedziemy korzystać z HashSet) wystarczy wywołać konstruktor:

Set<String> set = new HashSet<String>();

Do dodawania elementu służy metoda add:

set.add("jeden");

Pobieranie elementów odbywa się z użyciem iteratora, np w pętli for:

for (String string : set) {
    ...
}

Stan zbioru możemy sprawdzić korzystając z pomocniczych metod:

int iloscElementow = set.size();
boolean zbiorJestPusty = set.isEmpty();

Map (java.util.Map)

Mapy nie imlementują wprawdzie interfejsu Collection, ale są także częścią Java Collections API. Przechowują one mapowania pomiędzy kluczami, a wartościami. Zarówno dodanie elementu, jak i jego pobranie wymaga podania klucza. Kluczem może być dowolny obiekt. Elementy w Mapach mogą się powtarzać, pod warunkiem, że sa one wartościami. Klucze nie mogą się powtarzać. Mapy mają wiele wspólnego z omawianymi wyżej zbiorami (można o nich myśleć jak o zbiorach powiązań pomiędzy kluczem i wartością), przez co jak pewnie zauważysz dzielą wiele cech wspólnych.

Klasa Cechy Zastosowanie
HashMap
  • mapa nieposortowana
  • kolejność iteracji nieokreślona, może się zmieniać
  • dodanie elementu oraz sprawdzenie czy klucz istnieje ma złożoność O(1)
  • pobranie kolejnego elementu ma złożoność O(h/n), gdzie h to parametr wewnętrzny
Ogólny przypadek, tworzenie lokalnej pamięci podręcznej czy ‚słownika’ o ograniczonym rozmiarze, zliczanie wg klucza.
LinkedHashMap
  • analogiczne, jak HashMap (dziedziczy po niej)
  • kolejność kluczy używając iteratora jest deterministyczna i powtarzalna (zawsze będziemy przechodzić przez klucze w tej samej kolejności)
  • pobranie kolejnego elementu ma złożoność O(1)
Podobnie jak LinkedHashSet, rzadziej znana i stosowana. Przydatna, jeśli potrzebujemy iterować po kluczach w założonej kolejności.
TreeMap
  • Przechowuje elementy posortowane wg porządku naturalnego kluczy (jeśli implementują one interjfejs Comparable, w przeciwnym wypadku porządek jest nieokreslony)
  • Wszystkie operacje (dodanie, sprawdzenie czy klucz istnieje oraz pobranie kolejnego elementu) mają złożoność O(log n)
Jeśli potrzebujemy, aby nasz zbiór był posortowany wg kluczy bez dodatkowych operacji (sortowanie nastepuje już w momencie dodania elementu) oraz iterować po posortowanej kolekcji.
 Hashtable
  •  Historyczna klasa, która w Javie 1.2 została włączona do Java Collecion API
Oficjalnie zaleca się korzystanie z HashSet w większości wypadków zamiast Hashtable

Aby utworzyć nową mapę (do przykładów bedziemy korzystać z HashMap) wystarczy wywołać konstruktor:

Map<Integer, String> map= new HashMap<Integer,String>();

Do dodawania elementu służy metoda put:

map.put(1, "jeden");

Pobieranie elementów odbywa się z użyciem metody get, podając klucz jako argument:

String string = map.get(1)

Stan mapy możemy sprawdzić korzystając z pomocniczych metod:

int iloscElementow = map.size();
boolean mapaJestPusta = map.isEmpty();
boolean zawieraKlucz = map.containsKey(1);
boolean zawieraWartosc = map.containsValue("jeden");

Kolejki (java.util.Queue)

Kolejki dzielą się na dwa typy – LIFO (last-in, first-out) oraz FIFO (first-in, first-out). Przykładem kolejki LIFO jest stos, mówiąc o kolejce często mamy na myśli kolejkę FIFO. Co ciekawe, w języku Java klasa Stack implementuje interfejs listy, a nie kolejki. Drugą ciekawostką jest fakt, że klasa LinkedList także implementuje interfejs Queue (ponieważ była ona już omówiona wczesniej, nie będziemy powtarzać).

W kolejkach wyróżniamy głowę oraz ogon – stadardowo interfejs Queue pozwala na pracę tylko z głową, interfejs Dequeue (który dziedziczy po Queue) pozwala na pracę zarówno z głową jak i ogonem.

Ideą kolejek jest przechowywanie ‚kolejki’ obiektów do przetworzenia w określonej kolejności, stanowią pewnego rodzaju bufor obiektów.

Klasa Cechy Zastosowanie
ArrayDeque
  • Przechowuje elementy w tablicy
  • Ma nieograniczoną pojemność (automatycznie rozszerza tablicę wpamięci)
  • Dodanie i podejrzenie elementu mają złożoność O(1)
  • Pobranie elementu ma złożoność O(n)
Uniwersalna implementacja pasująca do większości przypadków, działa na zasadzie FIFO, możliwość pracy także z ogonem powoduje że sprawdzi się także jako kolejka LIFO
PriorityQueue
  • Nie umożliwa przechowywania wartości null
  • Nie jest stricte kolejką FIFO, ponieważ kolejność elementów zalezy od ‚priorytetu’ – w najprostszym przypadku elementy są po prostu sortowane
 Używana najczęściej jako kolejka zadań, które mają określony priorytet, tzn jedne są ważniejsze od innych.
Stack
  •  Kolejka typu LIFO (stos)
  • API niezgodne z interfejsem Queue!
  • implementuje interfejs List, dziedziczy po Vector
 Implementacja ta była od początku Javy, przed powstaniem Collections API. Obecnie raczej zalecane jest korzystanie np. z ArrayDequeue

Poniższy opis obejmuje jedynie interfejs Queue, którego klasa Stack nie implementuje. Aby z niej skorzystać, zapoznaj się z dokumentacją API.

Aby utworzyć nową kolejkę (do przykładów bedziemy używać ArrayDequeue) wystarczy wywołać konstruktor:

Queue<String> kolejka = new ArrayDequeue<String>();

Do dodawania elementu służą metody add oraz offer:

kolejka.add("jeden"); //jeżeli kolejka jest pełna, rzuci wyjątek
kolejka.offer("jeden"); //nie rzuci wyjątku, a jedynie zwróci false

Pobieranie elementów odbywa się z użyciem metod remove i poll:

String string = kolejka.remove(); //jeżeli kolejka jest pusta, rzuci wyjątek
String string = kolejka.poll(); //jeżeli kolejka jest pusta, zwróci null

Kolejka pozwala nam także ‚podejrzeć’ kolejny element nie usuwając go z kolejki, służą do tego metody element oraz peek:

String string = kolejka.element(); //jeżeli kolejka jest pusta, rzuci wyjątek
String string = kolejka.peek(); //jeżeli kolejka jest pusta, zwróci null

Stan kolejki możemy sprawdzić korzystając z pomocniczych metod:

int iloscElementow = kolejka.size();
boolean kolejkaJestPusta = kolejka.isEmpty();

Podsumowanie

W ramach podsumowania zbierzemy informacje o wszystkich omówionych rodzajach kolekcji w formie tabeli. Warto też zapoznać się z oficjalną dokumentacją na temat collections API, dostępnńą pod adresem http://docs.oracle.com/javase/8/docs/technotes/guides/collections/ .

Kolekcja Cechy implementacje
Object[] uporządkowana
dostęp losowy (n’ty element)
może zawierać duplikaty
może zawierać wartości null
struktura języka
może przechowywać prymitywy
niezmienna pojemność
nie dotyczy
List uporządkowana
dostęp losowy (n’ty element)
może zawierać duplikaty
ArrayList
LinkedList
Stack
Vector
Set zbiór nieuporządkowany
nie może zawierać duplikatów
dostęp do elementów możliwy tylko poprzez iterator
HashSet
LinkedHashSet
TreeSet
Map mapuje klucz na wartość
możliwe duplikaty (tylko w przypadku wartości, klucze nie mogą się powtarzać)
dostęp do obiektów poprzez klucz lub iteracyjnie
HashMap
LinkedHashMap
TreeMap
Queue kolejka first-in, first-out
możliwość pobierania pierwszego elementu poprzez jego ‚zdjęcie’ z kolejki lub podejrzenie
ArrayDequeue
PriorityQueue
LinkedList
  •  
  •  
  •  
  •  
  •  
  • Aga

    WIELKIE dzięki za ten wpis, w piątek mam rozmowę, a tu akurat wszystko w jednym miejscu! :D

  • Arek

    Pytanie za 5 punktów :)
    Pobieram sobie zwykłym jdbc jakąś tablice z bazy, powiedzmy „select * form klienci”, którą chce później wyświetlić w postaci tabelki na ekranie. Nie wiem ile tam może być rekordów, może być 100, a może być milion ;) Pytanie mam takie, do jakiego typu dodawać moich klientów ? Z tego co wiem ArrayList przy dodawaniu „rezerwuje” sobie sporo pamięci, co może skutkować stackoverflow, z kolei LinkedList jest dość wolna jeśli chodzi o przeszukiwanie, czy najlepszą opcją jest w takim wypadku ładowanie elementów do HashSeta ?

    • Prawidłową odpowiedzią jest: w prawidłowej sytuacji nie ma to znaczenia jakiej kolekcji użyjesz ;)

      Ogólnie rzecz biorąc nie powinieneś NIGDY przetwarzać całej tabeli, w szczególności jeśli może tam być sporo danych. O ile nie masz przypadku, w którym absolutnie musisz przetwarzać wszystkie rekordy jednocześnie (a jeśli taki masz, rozważ zmianę technologii i użycie np. Hadoop albo innych technologii związanych z big data), to powinieneś pobieranie z bazy danych dzielić na strony. Wtedy mamy już kolekcje o przewidywalnej wielkości, więc możemy uprzednio zarezerwować pamięć np. ArrayListy: ArrayList lista = new ArrayList(100); (https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#ArrayList-int-) .

      Jeśli chodzi o wspomniane rezerwowanie pamięci, ArrayList początkowo rezerwuje więcej pamięci niż przechowuje elementów po to, aby przy dodaniu nie powiększać pamięci za każdym razem. Nie stracisz więc wiele pamięci, ale dodawanie w pętli może być nieefektywne czasowo. Niestety dokumentacja nie mówi nic o liczbach w tym zakresie, cytując: „Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.”

      Kilka linków do dalszej lektury:
      http://java.avdiel.com/Tutorials/JDBCPaging.html
      http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.core-concepts
      http://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#setMaxRows-int-
      http://codefutures.com/spring-jdbc-pagination/

  • stary wyga branzy IT

    Dorzuccie cos o roznicy w synchronizacji Vectora w stosunku do ArrayListy

    • Współbieżność to dużo szerszy temat i obawiam się, że umieszczenie tutaj informacji o tym, że Vector jest synchronizowany a ArrayLista nie wprowadziłoby więcej zamieszania niż wartości dodanej. Będzie o tym na pewno kiedy będziemy pisać o współbieżności i synchronizacji wątków. Póki co dla uproszczenia pominęliśmy tą informację. Ponadto to, co jest napisane nadal jest prawdą – nawet jeśli potrzeba użyć kolekcji, której metody są synchronizowane, najprawdopodobniej dużo lepiej sprawdzi się ArrayList po ‚przetworzeniu’ metodą Collections.synchronizedList niż sam Vector.

      • stary wyga branzy IT

        Dzięki za odpowiedź. Świetnie! Oczywiście chętnie poczytam sobie o concurrency w waszym wydaniu. Jak dla mnie mogło tu nawet nie być wzmianki o Vectorze bo to i tak jest klasa obsolete. Wielki plus za to podsumowanie. Chylę czoła.

  • alekzander

    Cześć, wkradł się mały błąd: FIFO (first-in, last-out) – first-in, first-out.

  • staszko032

    „Stan zbioru możemy sprawdzić korzys­ta­jąc z pomoc­niczych metod:

    int iloscElementow = set.size();
    boolean listaJestPusta = set.isEmpty();”

    Proponuję zmienić nazwę zmiennej na zbiorJestPusty, podobnie dla kolejek niżej. Aktualna wersja może być trochę myląca ;)

    • Faktycznie mogło to być nieco mylące, poprawiliśmy, dzięki!

  • DS

    Myślę że warto wspomnieć przy okazji kolekcji że są hashtable jest thread-safe a hashmap nie jest.

    • Dzięki za zwrócenie uwagi – na blogu nie poruszaliśmy jeszcze szeroko zagadnień związanych z wielowątkowością, thread safety itp, w tym momencie ta informacja niewiele by większości czytelników mówiła. Ale na pewno o tym wspomnimy przy stosownej okazji. Dzięki!

  • Daniel

    Hej :) Przy LinkedHashMap -> zastosowania chyba powinno być „Podobnie jak LinkedHasSet…”. Przy okazji miło mi wspomnieć, że Wasza strona jest dla mnie prawdziwą inspiracją :)

  • rangunim1

    MAM problem z takim przypadkiem:

    import java.util.*;
    public class Test {
    public static void main(String[] args) {
    Set set = new HashSet();
    set.add(1);
    set.add(7);
    set.add(2);

    for(Integer l : set) {
    System.out.println(l);
    } } }

    Ktoś może mi wytumaczyć dlaczego w tym secie są sortowane wartości (wyświetli się 1,2,7), mimo iż to HashSet?

  • Nulka

    Hej ;) a mozna jakos sprawdzić wartość wybranego elementu kolejki ?