89. Czym różni się ArrayList od LinkedList?
To jedno z najpopularniejszych pytań, obok pytania o wewnętrzną strukturę HashMap . Żaden wywiad nie jest kompletny bez niego, dlatego odpowiedź na nie powinna „odbić się od zębów”. Oprócz oczywistych – różnych nazw – różnią się one budową wewnętrzną. Wcześniej badaliśmy wewnętrzną strukturę zarówno ArrayList , jak i LinkedList , więc nie będę wchodził w szczegóły ich implementacji. Przypominam, że ArrayList implementowany jest w oparciu o wewnętrzną tablicę, która jest zwiększana w miarę potrzeb według wzoru:<размерТекущегоМассива> * 3 / 2 + 1
Jednocześnie LinkedList jest realizowany w oparciu o wewnętrzną listę podwójnie połączoną, czyli każdy element posiada link do poprzedniego i następnego, z wyłączeniem wartości będących początkiem/końcem listy. Ludzie lubią zadawać to pytanie w formacie: „Co jest lepsze – ArrayList czy LinkedList ?”, mając nadzieję, że Cię złapią. W końcu, jeśli wskażesz jedną z nich jako odpowiedź, będzie to odpowiedź błędna. Zamiast tego powinieneś wyjaśnić, o jakiej konkretnej sytuacji mówisz - dostęp do indeksu lub wstawienie na środek listy. W zależności od odpowiedzi będziesz mógł uzasadnić swój wybór. Opisałem wcześniej, jak ArrayList i LinkedList działają w tej czy innej sytuacji. Podsumujmy to, umieszczając je na tej samej stronie dla porównania: Dodanie elementu (dodaj)
-
Добавление нового element без указания индекса Jak местоположения будет происходить автоматически в конец обоих списков. В LinkedList новый элемент станет новым хвостом (происходит только перезаписывание пары ссылок — алгоритмическая сложность O(1)).
В ArrayList будет добавлен новый элемент в последнюю пустую ячейку массива — O(1).
-
Добавление element по индексу Jak правило подразумевает вставку примерно в середину списка. В LinkedList сперва будет вестись поиск нужного места с помощью перебора элементов с “хвоста” и “головы” — O(n/2), а после — вставка значения путем переопределения ссылок элементов, между которыми вставляется новый — O(1). Суммарная алгоритмическая сложность данного действия будет O(n/2).
ArrayList в данной ситуации по индексу находит элемент — O(1), и все элементы справа (включая элемент, который уже хранится по данному индексу) двигаются на одну единицу вправо (при этом возможно понадобится создание нового списка и копирование элементов в него) — O(n/2). Суммарная сложность — O(n/2). -
Добавление element в начало списка в LinkedList будет ситуация схожая с добавлением в конец: новый элемент станет новой “головой” — O(1), в то же время когда ArrayList-у нужно будет двигать все элементы вправо — O(n).
90. Czym różni się ArrayList od HashSet?
Jeśli można by porównać ArrayList i LinkedList pod względem operacji - co jest lepsze - to porównanie ArrayList z HashSet nie jest takie proste, ponieważ są to zupełnie różne kolekcje. Można porównać jedno słodkie danie z drugim, ale sprawdzi się z daniem mięsnym - są zbyt różne. Spróbuję jednak podać pewne różnice między nimi:-
ArrayList implementuje interfejs List , podczas gdy HashSet implementuje interfejs Set ;
-
W ArrayList dostęp jest możliwy poprzez indeks elementu: operacja get ma złożoność algorytmiczną O(1) , a w HashSet wymagany element można uzyskać tylko metodą brute-force i jest to od O(1) do O(n) ;
-
ArrayList pozwala na duplikowanie elementów. W HashSet wszystkie elementy są unikalne: dodanie elementu do HashSet , którego odpowiednik jest już obecny w kolekcji, nie będzie działać (duplikaty są sprawdzane za pomocą hashcode, stąd nazwa tej kolekcji);
-
ArrayList jest zaimplementowany przy użyciu tablicy wewnętrznej, a HashSet jest zaimplementowany przy użyciu wewnętrznej HashMap ;
-
ArrayList utrzymuje kolejność wstawiania elementów, podczas gdy HashSet jest zestawem nieuporządkowanym i nie zachowuje kolejności elementów;
-
ArrayList dopuszcza dowolną liczbę pustych wartości (null), do HashSet można wstawić tylko jedną wartość null (w końcu unikalność elementów).
91. Dlaczego w Javie istnieje taka różnorodność implementacji tablic dynamicznych?
Cóż, to jest raczej pytanie filozoficzne. Dlaczego wymyślają tak wiele różnych nowych technologii? Dla komfortu. Właściwie to samo dotyczy dużej liczby implementacji tablic dynamicznych. Żadnego z nich nie można nazwać najlepszym i idealnym. Każdy ma przewagę w jakiejś konkretnej sytuacji. A naszym zadaniem jest poznać ich różnice, mocne/słabe strony, aby móc zastosować najodpowiedniejszy w danej sytuacji.92. Dlaczego w Javie istnieje tak wiele różnych implementacji przechowywania klucz-wartość?
Tutaj sytuacja jest taka sama jak w przypadku implementacji tablic dynamicznych. Nie ma nikogo najlepszego: każdy ma mocne i słabe strony. Musimy oczywiście maksymalnie wykorzystać nasze mocne strony. Przykład: pakiet współbieżny, który zawiera wiele technologii wielowątkowych, ma własne kolekcje współbieżne . Ta sama ConcurrentHashMap ma przewagę w bezpieczeństwie wielowątkowej pracy z danymi w porównaniu ze zwykłą HashMap , ale w środowisku nie-wielowątkowym traci na szybkości. Otóż wdrożenia, które w żadnej sytuacji nie są najmocniejsze, stopniowo odchodzą do lamusa. Przykład: Hashtable pierwotnie miał być bezpieczną wątkowo HashMap , ale ConcurrentHashMap radził sobie lepiej w środowisku wielowątkowym, a Hashtable ostatecznie został zapomniany i nie był już używany.93. Jak posortować zbiór elementów?
Pierwszą rzeczą do powiedzenia jest to, że klasa elementu kolekcji musi implementować interfejs Comparable i jego metodę CompareTo . Lub potrzebujesz klasy, która implementuje Comaprator z jego metodą porównawczą . Więcej na ich temat przeczytacie w tym poście . Obie metody określają sposób porównywania obiektów danego typu. Podczas sortowania jest to niezwykle ważne, ponieważ należy zrozumieć zasadę, według której można porównywać elementy. Głównym sposobem na to jest zaimplementowanie Comparable , zaimplementowane bezpośrednio w klasie, którą chcesz posortować. Jednocześnie korzystanie z komparatora jest mniej powszechne. Załóżmy, że używasz klasy z jakiejś biblioteki, która nie ma implementacji Comparable , ale musisz ją jakoś posortować. Nie mając możliwości zmiany kodu tej klasy (poza jego rozszerzeniem) można napisać implementację Comparator , w której wskazujesz na jakiej zasadzie chcesz porównywać obiekty tej klasy. I jeszcze jeden przykład. Załóżmy, że potrzebujesz różnych zasad sortowania obiektów tego samego typu, więc piszesz kilka komparatorów , których używasz w różnych sytuacjach. Z reguły wiele klas od razu implementuje interfejs Comparable - ten sam String . Właściwie, korzystając z nich, nie musisz się martwić, jak je porównać. Po prostu je bierzesz i używasz. Pierwszym i najbardziej oczywistym sposobem jest użycie kolekcji takiej jak TreeSet lub TreeMap , która przechowuje elementy w już posortowanej kolejności zgodnie z komparatorem klas elementów. Pamiętaj, że TreeMap sortuje klucze, ale nie wartości. Jeśli użyjesz implementacji Comparator zamiast Comparable , podczas tworzenia będziesz musiał przekazać jej obiekt do konstruktora kolekcji:TreeSet treeSet = new TreeSet(customComparator);
A co jeśli masz inny rodzaj kolekcji? Jak to posortować? W tym wypadku odpowiednia jest druga metoda klasy użytkowej Collections – metoda sort() . Jest statyczny, więc wystarczy nazwa klasy i metoda, do której przekazywana jest wymagana lista. Na przykład:
Collections.sort(someList);
Jeśli nie używasz Comparable , ale raczej implementację Comparator , musisz przekazać ją jako drugi parametr:
Collections.sort(someList, customComparator);
W rezultacie zmieni się wewnętrzna kolejność elementów przekazanej listy: zostanie ona posortowana według komparatora elementów. Zwracam uwagę, że przenoszona lista elementów musi być zmienna, tj. mutable, w przeciwnym razie metoda nie będzie działać i zostanie zgłoszony wyjątek UnsupportedOperationException . Trzecim sposobem możesz użyć operacji sortowania Stream , która sortuje elementy kolekcji, jeśli używana jest implementacja Comparable :
someList = someList.stream().sorted().collect(Collectors.toList());
jeśli komparator :
someList = someList.stream().sorted(customComparator).collect(Collectors.toList());
Więcej o Streamie możesz przeczytać w tym artykule . Czwartą metodą jest ręczne wdrożenie sortowania, takiego jak sortowanie bąbelkowe lub sortowanie przez scalanie .
Obiekt klasy. Równa się i HashCode
94. Podaj krótki opis obiektu klasy w Javie
W drugiej części analizy mówiliśmy już o metodach klasy Object i przypomnę, że klasa Object jest przodkiem wszystkich klas w Javie. Ma 11 metod, które odpowiednio są dziedziczone przez wszystkie klasy. Informacje o wszystkich 11 metodach można znaleźć w drugiej części dyskusji.95. Do czego służą znaki Equal i HashCode w Javie?
hashCode() to metoda klasy Object , która jest dziedziczona przez wszystkie klasy. Jego zadaniem jest wygenerowanie pewnej liczby reprezentującej konkretny obiekt. Przykładem użycia tej metody jest jej użycie w HashMap na obiekcie kluczowym w celu dalszego określenia lokalnego kodu skrótu, który określi komórkę wewnętrznej tablicy (zasobnika), w której będzie przechowywana para. O działaniu HashMap szczegółowo pisaliśmy w części 9 analizy , więc nie będziemy się nad tym zbytnio rozwodzić. Z reguły metoda ta jest stosowana w metodzie równości() jako jedno z jej głównych narzędzi do określania tożsamości obiektów. quals() to metoda klasy Object , której zadaniem jest porównywanie obiektów i określanie, czy są one równe, czy nie. Metodę tę stosuje się wszędzie tam, gdzie musimy porównać obiekty, ponieważ zwykłe porównanie za pomocą == nie jest odpowiednie dla obiektów, ponieważ porównuje tylko linki do nich.96. Opowiedz nam o umowie pomiędzy Equals i HashCode w Javie?
Pierwszą rzeczą, którą powiem, jest to, że aby metody równości() i hashCode() działały poprawnie , muszą zostać poprawnie zastąpione. Następnie muszą przestrzegać zasad:- identyczne obiekty, dla których porównanie przez równanie zwraca wartość true, muszą mieć identyczne kody skrótu;
- obiekty z tymi samymi kodami skrótu nie zawsze mogą być równe.
GO TO FULL VERSION