W języku Java, TreeSet jest jednym z implementacji interfejsu Set, który przechowuje unikalne elementy w uporządkowany sposób. Jest częścią pakietu java.util i jest oparty na strukturze danych zwanej drzewem binarnym poszukiwań (ang. Binary Search Tree, BST). W tym artykule przyjrzymy się szczegółowo, czym jest TreeSet, jak działa, jakie ma cechy oraz jak można go wykorzystać w praktyce.
1. Czym jest TreeSet?
TreeSet to implementacja kolekcji Set, co oznacza, że nie dopuszcza duplikatów – w zbiorze mogą znaleźć się tylko unikalne elementy. Co wyróżnia TreeSet spośród innych implementacji tego interfejsu, jak np. HashSet? Przede wszystkim porządek. Elementy w TreeSet są przechowywane w naturalnym porządku, tzn. w porządku rosnącym, chyba że dostarczymy własny komparator.
TreeSet działa na zasadzie drzewa binarnego, co zapewnia logarytmiczny czas wyszukiwania, wstawiania i usuwania elementów. Drzewo binarne poszukiwań, na którym bazuje ta struktura, zapewnia, że dla każdego węzła lewy potomek jest mniejszy, a prawy potomek większy od węzła.
2. Podstawowe właściwości TreeSet
Unikalność elementów
Podobnie jak w przypadku innych implementacji Set, TreeSet przechowuje tylko unikalne elementy. Próba dodania duplikatu do TreeSet nie spowoduje zmiany w zbiorze.
Porządek elementów
Domyślnie elementy w TreeSet są przechowywane w porządku rosnącym, co oznacza, że na początku najmniejszy element znajduje się na początku zbioru, a największy na końcu. Kolekcja ta zapewnia, że elementy są uporządkowane.
Brak elementów null
Wartością, którą nie można przechowywać w TreeSet, jest null. Próba dodania elementu null do TreeSet spowoduje wyrzucenie wyjątku NullPointerException.
Wydajność
Dzięki zastosowaniu drzewa binarnego poszukiwań, TreeSet oferuje wydajność w operacjach dodawania, usuwania i wyszukiwania elementów na poziomie logarytmicznym, czyli O(log n), gdzie n to liczba elementów w zbiorze. Ta wydajność sprawia, że TreeSet jest dobrym wyborem, gdy zależy nam na utrzymaniu porządku i szybkim dostępie do elementów.
3. Tworzenie TreeSet
Domyślny konstruktor
Tworzenie pustego TreeSet bez dostarczania komparatora:
TreeSet<Integer> set = new TreeSet<>();W tym przypadku elementy będą przechowywane w naturalnym porządku, a więc dla liczb całkowitych będą posortowane rosnąco.
TreeSet z komparatorem
Jeśli chcemy, aby elementy w TreeSet były przechowywane w niestandardowym porządku, możemy dostarczyć komparator:
TreeSet<String> set = new TreeSet<>(Comparator.reverseOrder());Powyższy przykład utworzy TreeSet, który będzie przechowywał elementy w porządku malejącym, na podstawie komparatora, który porównuje elementy w odwrotnej kolejności.
TreeSet z kolekcji
Możemy również stworzyć TreeSet z istniejącej kolekcji:
List<Integer> list = Arrays.asList(5, 1, 3, 7, 2);TreeSet<Integer> set = new TreeSet<>(list);
W wyniku powyższego kodu elementy z listy zostaną dodane do TreeSet, który automatycznie posortuje je w porządku rosnącym.
4. Dodawanie, usuwanie i sprawdzanie elementów
Dodawanie elementów
Dodawanie elementów do TreeSet odbywa się za pomocą metody add:
TreeSet<Integer> set = new TreeSet<>();set.add(10);set.add(20);set.add(5);
Jeśli spróbujemy dodać element, który już istnieje w zbiorze, add zwróci false i element nie zostanie dodany.
Usuwanie elementów
Aby usunąć element, używamy metody remove:
set.remove(20); // Usuwa element o wartości 20Sprawdzanie obecności elementów
Za pomocą metody contains możemy sprawdzić, czy dany element znajduje się w zbiorze:
boolean exists = set.contains(10); // Zwróci true, jeśli 10 znajduje się w zbiorzeCzyszczenie całego zbioru
Metoda clear usuwa wszystkie elementy z TreeSet:
set.clear(); // Usuwa wszystkie elementy5. Operacje na TreeSet
TreeSet oferuje kilka przydatnych metod umożliwiających manipulację i przetwarzanie danych.
Pierwszy i ostatni element
Za pomocą metod first() i last() możemy uzyskać odpowiednio pierwszy i ostatni element w zbiorze:
Integer first = set.first(); // Pierwszy elementInteger last = set.last(); // Ostatni element
Subset – podzbiór
Metoda subSet pozwala na utworzenie podzbioru z określonego zakresu elementów:
TreeSet<Integer> subset = set.subSet(5, 15); // Podzbiór od 5 do 15 (wyłącznie 15)Higher i lower
Metody higher() i lower() pozwalają na znalezienie elementu, który jest odpowiednio większy lub mniejszy od podanego:
Integer higher = set.higher(10); // Element większy niż 10Integer lower = set.lower(10); // Element mniejszy niż 10
PollFirst i pollLast
Metody pollFirst() i pollLast() usuwają i zwracają odpowiednio pierwszy lub ostatni element w zbiorze:
Integer firstPoll = set.pollFirst(); // Usuwa i zwraca pierwszy elementInteger lastPoll = set.pollLast(); // Usuwa i zwraca ostatni element
6. Porównanie TreeSet i innych implementacji Set
Warto zastanowić się, kiedy warto używać TreeSet w porównaniu do innych implementacji Set, takich jak HashSet.
TreeSet vs HashSet
- HashSet przechowuje elementy bez jakiegokolwiek porządku, co sprawia, że operacje takie jak dodawanie, usuwanie i wyszukiwanie są szybsze (średnio O(1)), ale nie zapewnia sortowania elementów.
- TreeSet przechowuje elementy w porządku naturalnym lub zgodnym z komparatorem, zapewniając porządek w zbiorze, ale operacje są wolniejsze (O(log n)).
TreeSet vs LinkedHashSet
- LinkedHashSet przechowuje elementy w porządku ich dodania, ale nie zapewnia sortowania. Umożliwia to szybki dostęp do elementów w porządku dodania (O(1)), ale bez sortowania.
- TreeSet, jak już wspomniano, przechowuje elementy w uporządkowany sposób, co jest idealne, jeśli zależy nam na posortowanych danych.
7. Zastosowania TreeSet
TreeSet znajduje szerokie zastosowanie w przypadkach, gdzie:
- Konieczne jest utrzymanie porządku w zbiorze elementów.
- Chcemy szybko znajdować najmniejsze lub największe elementy.
- Wymagana jest operacja dodawania, usuwania i wyszukiwania elementów w czasie logarytmicznym.
Przykłady zastosowań:
- Utrzymanie listy unikalnych elementów posortowanych alfabetycznie lub numerycznie.
- Zastosowanie w algorytmach, które wymagają dynamicznego utrzymywania posortowanej listy danych, np. w aplikacjach do analizy danych.
8. Podsumowanie
TreeSet w Javie to potężna struktura danych, która pozwala na przechowywanie unikalnych elementów w uporządkowany sposób. Dzięki zastosowaniu drzewa binarnego poszukiwań oferuje dobrą wydajność operacji wstawiania, usuwania i wyszukiwania. Choć jest wolniejszy niż HashSet w operacjach nie wymagających porządku, jego zdolność do utrzymywania posortowanych danych sprawia, że jest niezastąpiony w wielu sytuacjach.
Możliwość dostosowania porządku przechowywanych danych za pomocą komparatora oraz wsparcie dla różnych operacji, takich jak podzbiory czy wyszukiwanie większych lub mniejszych elementów, czyni TreeSet bardzo elastyczną i użyteczną strukturą w programowaniu w Javie.