26  Kolekcje

26.1 Interfejs Collection

A1. Napisz statyczną metodę printUnique(Collection<T> items), która przyjmuje generyczny interfejs Collection jako argument i wyświetla na ekranie każdy unikalny element z tej kolekcji dokładnie raz.

A2. Stwórz statyczną metodę countOccurrences(Collection<T> items, T element), która zwraca liczbę wystąpień danego elementu w podanej kolekcji. Funkcja powinna działać dla dowolnego typu obiektów przechowywanych w kolekcji.

A3. Zaimplementuj metodę removeEveryOther(Collection<T> items), która usuwa co drugi element z przekazanej kolekcji. Metoda powinna modyfikować oryginalną kolekcję, nie tworząc jej kopii.

26.2 Interfejs Iterable

B1. Napisz metodę reversePrint(Iterable items), która przyjmuje generyczny interfejs Iterable jako argument i wyświetla na ekranie elementy tej sekwencji w odwrotnej kolejności, niż zostały one przekazane.

B2. Stwórz funkcję findMax(Iterable numbers), która przeszukuje kolekcję typu Iterable zawierającą liczby i zwraca największą liczbę. Zakładamy, że elementy kolekcji są obiektami klasy Comparable.

B3. Zaimplementuj metodę countElements(Iterable items, Object element), która zlicza ile razy dany element pojawił się w kolekcji implementującej interfejs Iterable. Metoda powinna porównywać elementy przy użyciu metody equals.

26.3 Lista tablicowa ArrayList

C1. Stwórz metodę mergeLists, która przyjmuje dwie generyczne ArrayList<T> i zwraca nową ArrayList<T>, będącą połączeniem elementów z obu list. Upewnij się, że kolejność elementów z oryginalnych list jest zachowana w wynikowej liście.

C2. Napisz funkcję removeDuplicates, która przyjmuje ArrayList<T> i zwraca nową listę, z której usunięto wszystkie duplikaty, pozostawiając tylko unikalne elementy. Kolejność zachowanych elementów powinna odpowiadać ich pierwszemu wystąpieniu na oryginalnej liście.

C3. Zaimplementuj metodę countOccurrences, która przyjmuje ArrayList<T> i element typu T, a następnie zwraca liczbę wystąpień tego elementu w podanej liście.

26.4 Lista powiązana LinkedList

D1. Napisz metodę isPalindrome, która przyjmuje generyczną listę powiązaną (LinkedList<T> list) i zwraca true, jeśli lista jest palindromem, a false w przeciwnym przypadku. Lista jest palindromem, gdy czytana od przodu i od tyłu jest taka sama. Metoda powinna być jak najbardziej wydajna.

D2. Napisz metodę findCommonElements, która przyjmuje dwie generyczne listy powiązane (LinkedList<T> list1 i LinkedList<T> list2) i zwraca nową listę zawierającą elementy, które występują zarówno w list1, jak i list2. Elementy w zwróconej liście powinny być unikalne i nie muszą być posortowane. Metoda nie powinna modyfikować wejściowych list.

26.5 Zbiór bazujący na tablicy skrótów HashSet

E1. Napisz metodę findUniqueElements, która przyjmuje generyczną listę (List<T> list) i zwraca HashSet<T>, który zawiera tylko unikalne elementy z tej listy. Metoda powinna skutecznie eliminować duplikaty.

E2. Napisz metodę hasCommonElements, która przyjmuje dwa generyczne zbiory (HashSet<T> set1 i HashSet<T> set2) i zwraca true, jeśli oba zbiory mają przynajmniej jeden wspólny element, oraz false w przeciwnym przypadku.

E3. Napisz metodę unionSets, która przyjmuje dwie generyczne kolekcje typu HashSet<T> (HashSet<T> set1 i HashSet<T> set2) i zwraca nowy zbiór (HashSet<T>), który jest zbiorem unii obu wejściowych zbiorów. Wynikowy zbiór powinien zawierać wszystkie elementy, które występują w set1, set2, lub w obu tych zbiorach.

26.6 TreeSet

F1. Napisz metodę findElementsInRange, która przyjmuje TreeSet<T> oraz dwie wartości graniczne T lowerBound i T upperBound. Metoda powinna zwracać TreeSet<T>, który zawiera wszystkie elementy z pierwotnego zbioru, które mieszczą się w przedziale między lowerBound a upperBound (włącznie z obiema granicami).

F2. Napisz metodę removeMinMax, która przyjmuje TreeSet<T> i modyfikuje go przez usunięcie najmniejszego i największego elementu. Metoda powinna zwracać parę (np. klasę Pair lub dwuelementową tablicę/listę) zawierającą usunięte wartości. Jeśli zbiór ma mniej niż dwa elementy, metoda powinna zwrócić odpowiednie wartości nullowe lub wskazywać na błąd.

F3. Napisz metodę findClosestElement, która przyjmuje TreeSet<T> i wartość T target. Metoda powinna zwracać element zbioru, który jest najbliżej wartości target. W przypadku, gdy dwa elementy są równie blisko, metoda może zwrócić dowolny z nich. Zakładamy, że typ T pozwala na porównywanie wartości (np. poprzez implementację interfejsu Comparable<T>).

26.7 Queue

G1. Napisz metodę reverseQueue, która przyjmuje generyczną kolejkę (Queue<T> queue) i odwraca kolejność jej elementów. Metoda powinna wykonywać operacje odwracania w miejscu, nie używając dodatkowych kolekcji.

G2. Napisz metodę simulateSupermarketQueue, która przyjmuje generyczną kolejkę (Queue<Customer> customers) i dodatnią liczbę całkowitą n, reprezentujący liczbę dostępnych kas. Klasa Customer zawiera czas obsługi klienta. Metoda powinna zwrócić całkowity czas potrzebny do obsłużenia wszystkich klientów w kolejce, przy założeniu, że każda kasa obsługuje jednego klienta na raz i wybiera następnego klienta z przodu kolejki, gdy tylko zostanie zwolniona.

26.8 Deque

H1. Napisz metodę isSymmetric, która przyjmuje generyczny Deque (Deque<T> deque) i zwraca true, jeśli elementy w Deque są ułożone symetrycznie (tj. kolejność elementów jest taka sama, gdy czytamy je od przodu do tyłu i od tyłu do przodu). W przeciwnym przypadku metoda zwraca false.

H2. Napisz metodę removeEverySecondElement, która przyjmuje generyczny Deque (Deque<T> deque) i usuwa z niego co drugi element, zaczynając od drugiego elementu w kolejności, w której elementy zostały dodane. Metoda powinna modyfikować oryginalny Deque, nie zwracając nic.

H3. Napisz metodę swapEnds, która przyjmuje generyczny Deque (Deque<T> deque) i zamienia miejscami pierwszy element z ostatnim. Jeśli Deque zawiera mniej niż dwa elementy, metoda nie powinna nic robić. Metoda powinna modyfikować oryginalny Deque, nie zwracając nic.

26.9 Kolejka priorytetowa PriorityQueue

I1. Napisz metodę mergePriorityQueues, która przyjmuje dwie generyczne PriorityQueues (PriorityQueue<T> queue1 i PriorityQueue<T> queue2). Metoda powinna zwrócić nową PriorityQueue zawierającą wszystkie elementy z obu wejściowych kolejek. Zakładamy, że elementy w obu kolejnych mogą być porównywane między sobą.

26.10 Map

J1. Napisz metodę reverseMap, która przyjmuje generyczną mapę (Map<K, V> map) i zwraca nową mapę (Map<V, K>), gdzie każdy klucz staje się wartością, a każda wartość kluczem. Jeśli oryginalna mapa zawiera powtarzające się wartości, zachowaj tylko ostatnią parę klucz-wartość odwracając mapę.

J2. Napisz metodę findKeysWithMaxValue, która przyjmuje generyczną mapę (Map<K, V> map), gdzie wartości są porównywalne (implementują Comparable<V>). Metoda powinna zwracać listę wszystkich kluczy, które są powiązane z największą wartością w mapie.

J3. Napisz metodę groupByValue, która przyjmuje generyczną mapę (Map<K, V> map) i zwraca nową mapę (Map<V, List<K>>), gdzie każda wartość z oryginalnej mapy staje się kluczem w nowej mapie, a jej wartością jest lista kluczy, które były z nią powiązane w oryginalnej mapie.

26.11 HashMap

K1. Napisz metodę countValueOccurrences, która przyjmuje generyczną HashMap (HashMap<K, V> map) i zwraca nową HashMap (HashMap<V, Integer>), gdzie każdy klucz to wartość z oryginalnej mapy, a wartość to liczba wystąpień tej wartości w oryginalnej mapie.

K2. Napisz metodę swapKeysAndValues, która przyjmuje generyczną HashMap (HashMap<K, V> map) i zwraca nową HashMap (HashMap<V, K>), gdzie każdy klucz z oryginalnej mapy staje się wartością, a każda wartość kluczem. Zakładamy, że wszystkie wartości w oryginalnej mapie są unikalne.

K3. Napisz metodę aggregateValuesByKey, która przyjmuje generyczną HashMap (HashMap<K, Integer> map) oraz klucz (K key) i wartość (Integer value). Metoda powinna dodać wartość do obecnej wartości skojarzonej z kluczem. Jeśli klucz nie istnieje w mapie, powinien zostać dodany z podaną wartością. Metoda nie zwraca nic, modyfikując oryginalną mapę.

K4. Napisz metodę compareMaps, która przyjmuje dwie generyczne HashMap (HashMap<K, V> map1 i HashMap<K, V> map2) i zwraca true, jeśli obie mapy mają dokładnie te same pary klucz-wartość, oraz false w przeciwnym przypadku. Metoda powinna porównywać zarówno klucze, jak i wartości.

26.12 TreeMap

L1. Napisz metodę subMapInRange, która przyjmuje generyczną TreeMap (TreeMap<K, V> map), a także dwie wartości kluczy K startKey i K endKey. Metoda powinna zwrócić nową TreeMap, która zawiera wszystkie pary klucz-wartość z oryginalnej mapy, których klucze mieszczą się w zakresie od startKey do endKey, włącznie.

L2. Napisz metodę reverseOrderMap, która przyjmuje generyczną TreeMap (TreeMap<K, V> map). Metoda powinna zwrócić nową TreeMap, która zawiera wszystkie pary klucz-wartość z oryginalnej mapy, ale z odwróconą kolejnością kluczy.

Wskazówka: wykorzystaj Collections.reverseOrder().

L3. Napisz metodę calculateAverageValue, która przyjmuje generyczną TreeMap (TreeMap<K, Double> map). Metoda powinna obliczać i zwracać średnią wartość wszystkich wartości (typu Double) przechowywanych w mapie. Zakładamy, że mapa nie jest pusta.

26.13 Vector

M1. Stwórz funkcję concatenateVectors, która przyjmuje dwa obiekty Vector<T> i zwraca nowy Vector<T>, zawierający wszystkie elementy z pierwszego wektora, a po nich wszystkie elementy z drugiego wektora.

M2. Napisz metodę reverseVector, która przyjmuje obiekt Vector<T> i zwraca nowy Vector<T>, w którym kolejność elementów jest odwrócona względem oryginalnego wektora.

M3. Zaimplementuj funkcję filterVector, która przyjmuje Vector<T> i interfejs funkcyjny Predicate<T>. Funkcja powinna zwracać nowy Vector<T>, zawierający tylko te elementy z oryginalnego wektora, które spełniają warunek zdefiniowany przez Predicate<T>.