[Вопросы для собеседования](README.md) # Java Collections Framework + [Что такое _«коллекция»_?](#Что-такое-коллекция) + [Назовите основные интерфейсы JCF и их реализации.](#Назовите-основные-интерфейсы-jcf-и-их-реализации) + [Расположите в виде иерархии следующие интерфейсы: `List`, `Set`, `Map`, `SortedSet`, `SortedMap`, `Collection`, `Iterable`, `Iterator`, `NavigableSet`, `NavigableMap`.](#Расположите-в-виде-иерархии-следующие-интерфейсы-list-set-map-sortedset-sortedmap-collection-iterable-iterator-navigableset-navigablemap) + [Почему `Map` — это не `Collection`, в то время как `List` и `Set` являются `Collection`?](#Почему-map--это-не-collection-в-то-время-как-list-и-set-являются-collection) + [В чем разница между классами `java.util.Collection` и `java.util.Collections`?](#В-чем-разница-между-классами-javautilcollection-и-javautilcollections) + [Что такое «fail-fast поведение»?](#Что-такое-fail-fast-поведение) + [Какая разница между fail-fast и fail-safe?](#Какая-разница-между-fail-fast-и-fail-safe) + [Приведите примеры итераторов, реализующих поведение fail-safe](#Приведите-примеры-итераторов-реализующих-поведение-fail-safe) + [Чем различаются `Enumeration` и `Iterator`.](#Чем-различаются-enumeration-и-iterator) + [Как между собой связаны `Iterable` и `Iterator`?](#Как-между-собой-связаны-iterable-и-iterator) + [Как между собой связаны `Iterable`, `Iterator` и «for-each»?](#Как-между-собой-связаны-iterable-iterator-и-for-each) + [Сравните `Iterator` и `ListIterator`.](#Сравните-iterator-и-listiterator) + [Что произойдет при вызове `Iterator.next()` без предварительного вызова `Iterator.hasNext()`?](#Что-произойдет-при-вызове-iteratornext-без-предварительного-вызова-iteratorhasnext) + [Сколько элементов будет пропущено, если `Iterator.next()` будет вызван после 10-ти вызовов `Iterator.hasNext()`?](#Сколько-элементов-будет-пропущено-если-iteratornext-будет-вызван-после-10-ти-вызовов-iteratorhasnext) + [Как поведёт себя коллекция, если вызвать `iterator.remove()`?](#Как-поведёт-себя-коллекция-если-вызвать-iteratorremove) + [Как поведёт себя уже инстанциированный итератор для `collection`, если вызвать `collection.remove()`?](#Как-поведёт-себя-уже-инстанциированный-итератор-для-collection-если-вызвать-collectionremove) + [Как избежать `ConcurrentModificationException` во время перебора коллекции?](#Как-избежать-concurrentmodificationexception-во-время-перебора-коллекции) + [Какая коллекция реализует дисциплину обслуживания FIFO?](#Какая-коллекция-реализует-дисциплину-обслуживания-fifo) + [Какая коллекция реализует дисциплину обслуживания FILO?](#Какая-коллекция-реализует-дисциплину-обслуживания-filo) + [Чем отличается `ArrayList` от `Vector`?](#Чем-отличается-arraylist-от-vector) + [Зачем добавили `ArrayList`, если уже был `Vector`?](#Зачем-добавили-arraylist-если-уже-был-vector) + [Чем отличается `ArrayList` от `LinkedList`? В каких случаях лучше использовать первый, а в каких второй?](#Чем-отличается-arraylist-от-linkedlist-В-каких-случаях-лучше-использовать-первый-а-в-каких-второй) + [Что работает быстрее `ArrayList` или `LinkedList`?](#Что-работает-быстрее-arraylist-или-linkedlist) + [Какое худшее время работы метода `contains()` для элемента, который есть в `LinkedList`?](#Какое-худшее-время-работы-метода-contains-для-элемента-который-есть-в-linkedlist) + [Какое худшее время работы метода `contains()` для элемента, который есть в `ArrayList`?](#Какое-худшее-время-работы-метода-contains-для-элемента-который-есть-в-arraylist) + [Какое худшее время работы метода `add()` для `LinkedList`?](#Какое-худшее-время-работы-метода-add-для-linkedlist) + [Какое худшее время работы метода `add()` для `ArrayList`?](#Какое-худшее-время-работы-метода-add-для-arraylist) + [Необходимо добавить 1 млн. элементов, какую структуру вы используете?](#Необходимо-добавить-1-млн-элементов-какую-структуру-вы-используете) + [Как происходит удаление элементов из `ArrayList`? Как меняется в этом случае размер `ArrayList`?](#Как-происходит-удаление-элементов-из-arraylist-Как-меняется-в-этом-случае-размер-arraylist) + [Предложите эффективный алгоритм удаления нескольких рядом стоящих элементов из середины списка, реализуемого `ArrayList`.](#Предложите-эффективный-алгоритм-удаления-нескольких-рядом-стоящих-элементов-из-середины-списка-реализуемого-arraylist) + [Сколько необходимо дополнительной памяти при вызове `ArrayList.add()`?](#Сколько-необходимо-дополнительной-памяти-при-вызове-arraylistadd) + [Сколько выделяется дополнительно памяти при вызове `LinkedList.add()`?](#Сколько-выделяется-дополнительно-памяти-при-вызове-linkedlistadd) + [Оцените количество памяти на хранение одного примитива типа `byte` в `LinkedList`?](#Оцените-количество-памяти-на-хранение-одного-примитива-типа-byte-в-linkedlist) + [Оцените количество памяти на хранение одного примитива типа `byte` в `ArrayList`?](#Оцените-количество-памяти-на-хранение-одного-примитива-типа-byte-в-arraylist) + [Для `ArrayList` или для `LinkedList` операция добавления элемента в середину (`list.add(list.size()/2, newElement)`) медленнее?](#Для-arraylist-или-для-linkedlist-операция-добавления-элемента-в-середину-listaddlistsize2-newelement-медленнее) + [В реализации класса `ArrayList` есть следующие поля: `Object[] elementData`, `int size`. Объясните, зачем хранить отдельно `size`, если всегда можно взять `elementData.length`?](#В-реализации-класса-arraylist-есть-следующие-поля-object-elementdata-int-size-Объясните-зачем-хранить-отдельно-size-если-всегда-можно-взять-elementdatalength) + [Сравните интерфейсы `Queue` и `Deque`.](#Сравните-интерфейсы-queue-и-deque) + [Кто кого расширяет: `Queue` расширяет `Deque`, или `Deque` расширяет `Queue`?](#Кто-кого-расширяет-queue-расширяет-deque-или-deque-расширяет-queue) + [Почему `LinkedList` реализует и `List`, и `Deque`?](#Почему-linkedlist-реализует-и-list-и-deque) + [`LinkedList` — это односвязный, двусвязный или четырехсвязный список?](#linkedlist--это-односвязный-двусвязный-или-четырехсвязный-список) + [Как перебрать элементы `LinkedList` в обратном порядке, не используя медленный `get(index)`?](#Как-перебрать-элементы-linkedlist-в-обратном-порядке-не-используя-медленный-getindex) + [Что позволяет сделать `PriorityQueue`?](#Что-позволяет-сделать-priorityqueue) + [`Stack` считается «устаревшим». Чем его рекомендуют заменять? Почему?](#stack-считается-устаревшим-Чем-его-рекомендуют-заменять-Почему) + [Зачем нужен `HashMap`, если есть `Hashtable`?](#Зачем-нужен-hashmap-если-есть-hashtable) + [В чем разница между `HashMap` и `IdentityHashMap`? Для чего нужна `IdentityHashMap`?](#В-чем-разница-между-hashmap-и-identityhashmap-Для-чего-нужна-identityhashmap) + [В чем разница между `HashMap` и `WeakHashMap`? Для чего используется `WeakHashMap`?](#В-чем-разница-между-hashmap-и-weakhashmap-Для-чего-используется-weakhashmap) + [В `WeakHashMap` используются WeakReferences. А почему бы не создать `SoftHashMap` на SoftReferences?](#В-weakhashmap-используются-weakreferences-А-почему-бы-не-создать-softhashmap-на-softreferences) + [В `WeakHashMap` используются WeakReferences. А почему бы не создать `PhantomHashMap` на PhantomReferences?](#В-weakhashmap-используются-weakreferences-А-почему-бы-не-создать-phantomhashmap-на-phantomreferences) + [`LinkedHashMap` - что в нем от `LinkedList`, а что от `HashMap`?](#linkedhashmap---что-в-нем-от-linkedlist-а-что-от-hashmap) + [В чем проявляется «сортированность» `SortedMap`, кроме того, что `toString()` выводит все элементы по порядку?](#В-чем-проявляется-сортированность-sortedmap-кроме-того-что-tostring-выводит-все-элементы-по-порядку) + [Как устроен `HashMap`?](#Как-устроен-hashmap) + [Согласно Кнуту и Кормену существует две основных реализации хэш-таблицы: на основе открытой адресации и на основе метода цепочек. Как реализована `HashMap`? Почему, по вашему мнению, была выбрана именно эта реализация? В чем плюсы и минусы каждого подхода?](#Согласно-Кнуту-и-Кормену-существует-две-основных-реализации-хэш-таблицы-на-основе-открытой-адресации-и-на-основе-метода-цепочек-Как-реализована-hashmap-Почему-по-вашему-мнению-была-выбрана-именно-эта-реализация-В-чем-плюсы-и-минусы-каждого-подхода) + [Как работает `HashMap` при попытке сохранить в него два элемента по ключам с одинаковым `hashCode()`, но для которых `equals() == false`?](#Как-работает-hashmap-при-попытке-сохранить-в-него-два-элемента-по-ключам-с-одинаковым-hashcode-но-для-которых-equals--false) + [Какое начальное количество корзин в `HashMap`?](#Какое-начальное-количество-корзин-в-hashmap) + [Какова оценка временной сложности операций над элементами из `HashMap`? Гарантирует ли `HashMap` указанную сложность выборки элемента?](#Какова-оценка-временной-сложности-операций-над-элементами-из-hashmap-Гарантирует-ли-hashmap-указанную-сложность-выборки-элемента) + [Возможна ли ситуация, когда `HashMap` выродится в список даже с ключами имеющими разные `hashCode()`?](#Возможна-ли-ситуация-когда-hashmap-выродится-в-список-даже-с-ключами-имеющими-разные-hashcode) + [В каком случае может быть потерян элемент в `HashMap`?](#В-каком-случае-может-быть-потерян-элемент-в-hashmap) + [Почему нельзя использовать `byte[]` в качестве ключа в `HashMap`?](#Почему-нельзя-использовать-byte-в-качестве-ключа-в-hashmap) + [Какова роль `equals()` и `hashCode()` в `HashMap`?](#Какова-роль-equals-и-hashcode-в-hashmap) + [Каково максимальное число значений `hashCode()`?](#Каково-максимальное-число-значений-hashcode) + [Какое худшее время работы метода get(key) для ключа, которого нет в `HashMap`?](#Какое-худшее-время-работы-метода-getkey-для-ключа-которого-нет-в-hashmap) + [Какое худшее время работы метода get(key) для ключа, который есть в `HashMap`?](#Какое-худшее-время-работы-метода-getkey-для-ключа-который-есть-в-hashmap) + [Сколько переходов происходит в момент вызова `HashMap.get(key)` по ключу, который есть в таблице?](#Сколько-переходов-происходит-в-момент-вызова-hashmapgetkey-по-ключу-который-есть-в-таблице) + [Сколько создается новых объектов, когда вы добавляете новый элемент в `HashMap`?](#Сколько-создается-новых-объектов-когда-вы-добавляете-новый-элемент-в-hashmap) + [Как и когда происходит увеличение количества корзин в `HashMap`?](#Как-и-когда-происходит-увеличение-количества-корзин-в-hashmap) + [Объясните смысл параметров в конструкторе `HashMap(int initialCapacity, float loadFactor)`.](#Объясните-смысл-параметров-в-конструкторе-hashmapint-initialcapacity-float-loadfactor) + [Будет ли работать `HashMap`, если все добавляемые ключи будут иметь одинаковый `hashCode()`?](#Будет-ли-работать-hashmap-если-все-добавляемые-ключи-будут-иметь-одинаковый-hashcode) + [Как перебрать все ключи `Map`?](#Как-перебрать-все-ключи-map) + [Как перебрать все значения `Map`?](#Как-перебрать-все-значения-map) + [Как перебрать все пары «ключ-значение» в `Map`?](#Как-перебрать-все-пары-ключ-значение-в-map) + [В чем отличия `TreeSet` и `HashSet`?](#В-чем-отличия-treeset-и-hashset) + [Что будет, если добавлять элементы в `TreeSet` по возрастанию?](#Что-будет-если-добавлять-элементы-в-treeset-по-возрастанию) + [Чем `LinkedHashSet` отличается от `HashSet`?](#Чем-linkedhashset-отличается-от-hashset) + [Для `Enum` есть специальный класс `java.util.EnumSet`. Зачем? Чем авторов не устраивал `HashSet` или `TreeSet`?](#Для-enum-есть-специальный-класс-javautilenumset-Зачем-Чем-авторов-не-устраивал-hashset-или-treeset) + [Какие существуют способы перебирать элементы списка?](#Какие-существуют-способы-перебирать-элементы-списка) + [Каким образом можно получить синхронизированные объекты стандартных коллекций?](#Каким-образом-можно-получить-синхронизированные-объекты-стандартных-коллекций) + [Как получить коллекцию только для чтения?](#Как-получить-коллекцию-только-для-чтения) + [Напишите однопоточную программу, которая заставляет коллекцию выбросить `ConcurrentModificationException`.](#Напишите-однопоточную-программу-которая-заставляет-коллекцию-выбросить-concurrentmodificationexception) + [Приведите пример, когда какая-либо коллекция выбрасывает `UnsupportedOperationException`.](#Приведите-пример-когда-какая-либо-коллекция-выбрасывает-unsupportedoperationexception) + [Реализуйте симметрическую разность двух коллекций используя методы `Collection` (`addAll(...)`, `removeAll(...)`, `retainAll(...)`).](#Реализуйте-симметрическую-разность-двух-коллекций-используя-методы-collection-addall-removeall-retainall) + [Как, используя LinkedHashMap, сделать кэш c «invalidation policy»?](#Как-используя-linkedhashmap-сделать-кэш-c-invalidation-policy) + [Как одной строчкой скопировать элементы любой `collection` в массив?](#Как-одной-строчкой-скопировать-элементы-любой-collection-в-массив) + [Как одним вызовом из `List` получить `List` со всеми элементами, кроме первых и последних 3-х?](#Как-одним-вызовом-из-list-получить-list-со-всеми-элементами-кроме-первых-и-последних-3-х) + [Как одной строчкой преобразовать `HashSet` в `ArrayList`?](#Как-одной-строчкой-преобразовать-hashset-в-arraylist) + [Как одной строчкой преобразовать `ArrayList` в `HashSet`?](#Как-одной-строчкой-преобразовать-arraylist-в-hashset) + [Сделайте `HashSet` из ключей `HashMap`.](#Сделайте-hashset-из-ключей-hashmap) + [Сделайте `HashMap` из `HashSet>`.](#Сделайте-hashmap-из-hashsetmapentryk-v) ## Что такое _«коллекция»_? _«Коллекция»_ - это структура данных, набор каких-либо объектов. Данными (объектами в наборе) могут быть числа, строки, объекты пользовательских классов и т.п. [к оглавлению](#java-collections-framework) ## Назовите основные интерфейсы JCF и их реализации. На вершине иерархии в Java Collection Framework располагаются 2 интерфейса: `Collection` и `Map`. Эти интерфейсы разделяют все коллекции, входящие во фреймворк на две части по типу хранения данных: простые последовательные наборы элементов и наборы пар «ключ — значение» соответственно. Интерфейс `Collection` расширяют интерфейсы: + `List` (список) представляет собой коллекцию, в которой допустимы дублирующие значения. Реализации: + `ArrayList` - инкапсулирует в себе обычный массив, длина которого автоматически увеличивается при добавлении новых элементов. Элементы такой коллекции пронумерованы, начиная от нуля, к ним можно обратиться по индексу. + `LinkedList` (двунаправленный связный список) - состоит из узлов, каждый из которых содержит как собственно данные, так и две ссылки на следующий и предыдущий узел. + `Vector` — реализация динамического массива объектов, методы которой синхронизированы. + `Stack` — реализация стека LIFO (last-in-first-out). + `Set` (сет) описывает неупорядоченную коллекцию, не содержащую повторяющихся элементов. Реализации: + `HashSet` - использует HashMap для хранения данных. В качестве ключа используется добавляемый элемент, в качестве значения - заглушка Object. Из-за особенностей реализации порядок элементов не гарантируется при добавлении. + `LinkedHashSet` — гарантирует, что порядок элементов при обходе коллекции будет идентичен порядку добавления элементов. + `TreeSet` — предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». + `Queue` (очередь) предназначена для хранения элементов с предопределённым способом вставки и извлечения FIFO (first-in-first-out): + `PriorityQueue` — предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». + `ArrayDeque` — реализация интерфейса `Deque`, который расширяет интерфейс `Queue` методами, позволяющими реализовать конструкцию вида LIFO (last-in-first-out). Интерфейс `Map` реализован классами: + `Hashtable` — хэш-таблица, методы которой синхронизированы. Не позволяет использовать `null` в качестве значения или ключа и не является упорядоченной. + `HashMap` — хэш-таблица. Позволяет использовать `null` в качестве значения или ключа и не является упорядоченной. + `LinkedHashMap` — упорядоченная реализация хэш-таблицы. + `TreeMap` — реализация, основанная на красно-чёрных деревьях. Является упорядоченной и предоставляет возможность управлять порядком элементов в коллекции при помощи объекта `Comparator`, либо сохраняет элементы с использованием «natural ordering». + `WeakHashMap` — реализация хэш-таблицы, которая организована с использованием _weak references_ для ключей (сборщик мусора автоматически удалит элемент из коллекции при следующей сборке мусора, если на ключ этого элемента нет жёстких ссылок). [к оглавлению](#java-collections-framework) ## Расположите в виде иерархии следующие интерфейсы: `List`, `Set`, `Map`, `SortedSet`, `SortedMap`, `Collection`, `Iterable`, `Iterator`, `NavigableSet`, `NavigableMap`. + `Iterable` + `Collection` + `List` + `Set` + `SortedSet` + `NavigableSet` + `Map` + `SortedMap` + `NavigableMap` + `Iterator` [к оглавлению](#java-collections-framework) ## Почему `Map` — это не `Collection`, в то время как `List` и `Set` являются `Collection`? `Collection` представляет собой совокупность некоторых элементов. `Map` - это совокупность пар «ключ-значение». [к оглавлению](#java-collections-framework) ## В чем разница между классами `java.util.Collection` и `java.util.Collections`? `java.util.Collections` - набор статических методов для работы с коллекциями. `java.util.Collection` - один из основных интерфейсов Java Collections Framework. [к оглавлению](#java-collections-framework) ## Что такое «fail-fast поведение»? __fail-fast поведение__ означает, что при возникновении ошибки или состояния, которое может привести к ошибке, система немедленно прекращает дальнейшую работу и уведомляет об этом. Использование fail-fast подхода позволяет избежать недетерминированного поведения программы в течение времени. В Java Collections API некоторые итераторы ведут себя как fail-fast и выбрасывают `ConcurrentModificationException`, если после его создания была произведена модификация коллекции, т.е. добавлен или удален элемент напрямую из коллекции, а не используя методы итератора. Реализация такого поведения осуществляется за счет подсчета количества модификаций коллекции (modification count): + при изменении коллекции счетчик модификаций так же изменяется; + при создании итератора ему передается текущее значение счетчика; + при каждом обращении к итератору сохраненное значение счетчика сравнивается с текущим, и, если они не совпадают, возникает исключение. [к оглавлению](#java-collections-framework) ## Какая разница между fail-fast и fail-safe? В противоположность fail-fast, итераторы fail-safe не вызывают никаких исключений при изменении структуры, потому что они работают с клоном коллекции вместо оригинала. [к оглавлению](#java-collections-framework) ## Приведите примеры итераторов, реализующих поведение fail-safe Итератор коллекции `CopyOnWriteArrayList` и итератор представления `keySet` коллекции `ConcurrentHashMap` являются примерами итераторов fail-safe. [к оглавлению](#java-collections-framework) ## Чем различаются `Enumeration` и `Iterator`. Хотя оба интерфейса и предназначены для обхода коллекций между ними имеются существенные различия: + с помощью `Enumeration` нельзя добавлять/удалять элементы; + в `Iterator` исправлены имена методов для повышения читаемости кода (`Enumeration.hasMoreElements()` соответствует `Iterator.hasNext()`, `Enumeration.nextElement()` соответствует `Iterator.next()` и т.д); + `Enumeration` присутствуют в устаревших классах, таких как `Vector`/`Stack`, тогда как `Iterator` есть во всех современных классах-коллекциях. [к оглавлению](#java-collections-framework) ## Как между собой связаны `Iterable` и `Iterator`? Интерфейс `Iterable` имеет только один метод - `iterator()`, который возвращает `Iterator`. [к оглавлению](#java-collections-framework) ## Как между собой связаны `Iterable`, `Iterator` и «for-each»? Классы, реализующие интерфейс `Iterable`, могут применяться в конструкции `for-each`, которая использует `Iterator`. [к оглавлению](#java-collections-framework) ## Сравните `Iterator` и `ListIterator`. + `ListIterator` расширяет интерфейс `Iterator` + `ListIterator` может быть использован только для перебора элементов коллекции `List`; + `Iterator` позволяет перебирать элементы только в одном направлении, при помощи метода `next()`. Тогда как `ListIterator` позволяет перебирать список в обоих направлениях, при помощи методов `next()` и `previous()`; + `ListIterator` не указывает на конкретный элемент: его текущая позиция располагается между элементами, которые возвращают методы `previous()` и `next()`. + При помощи `ListIterator` вы можете модифицировать список, добавляя/удаляя элементы с помощью методов `add()` и `remove()`. `Iterator` не поддерживает данного функционала. [к оглавлению](#java-collections-framework) ## Что произойдет при вызове `Iterator.next()` без предварительного вызова `Iterator.hasNext()`? Если итератор указывает на последний элемент коллекции, то возникнет исключение `NoSuchElementException`, иначе будет возвращен следующий элемент. [к оглавлению](#java-collections-framework) ## Сколько элементов будет пропущено, если `Iterator.next()` будет вызван после 10-ти вызовов `Iterator.hasNext()`? Нисколько - `hasNext()` осуществляет только проверку наличия следующего элемента. [к оглавлению](#java-collections-framework) ## Как поведёт себя коллекция, если вызвать `iterator.remove()`? Если вызову `iterator.remove()` предшествовал вызов `iterator.next()`, то `iterator.remove()` удалит элемент коллекции, на который указывает итератор, в противном случае будет выброшено `IllegalStateException()`. [к оглавлению](#java-collections-framework) ## Как поведёт себя уже инстанциированный итератор для `collection`, если вызвать `collection.remove()`? При следующем вызове методов итератора будет выброшено `ConcurrentModificationException`. [к оглавлению](#java-collections-framework) ## Как избежать `ConcurrentModificationException` во время перебора коллекции? + Попробовать подобрать или реализовать самостоятельно другой итератор, работающий по принципу fail-safe. + Использовать `ConcurrentHashMap` и `CopyOnWriteArrayList`. + Преобразовать список в массив и перебирать массив. + Блокировать изменения списка на время перебора с помощью блока `synchronized`. Отрицательная сторона последних двух вариантов - ухудшение производительности. [к оглавлению](#java-collections-framework) ## Какая коллекция реализует дисциплину обслуживания FIFO? FIFO, First-In-First-Out («первым пришел-первым ушел») - по этому принципу построена коллекция `Queue`. [к оглавлению](#java-collections-framework) ## Какая коллекция реализует дисциплину обслуживания FILO? FILO, First-In-Last-Out («первым пришел, последним ушел») - по этому принципу построена коллекция `Stack`. [к оглавлению](#java-collections-framework) ## Чем отличается `ArrayList` от `Vector`? ## Зачем добавили `ArrayList`, если уже был `Vector`? + Методы класса `Vector` синхронизированы, а `ArrayList` - нет; + По умолчанию, `Vector` удваивает свой размер, когда заканчивается выделенная под элементы память. `ArrayList` же увеличивает свой размер только на половину. `Vector` это устаревший класс и его использование не рекомендовано. [к оглавлению](#java-collections-framework) ## Чем отличается `ArrayList` от `LinkedList`? В каких случаях лучше использовать первый, а в каких второй? `ArrayList` это список, реализованный на основе массива, а `LinkedList` — это классический двусвязный список, основанный на объектах с ссылками между ними. `ArrayList`: + доступ к произвольному элементу по индексу за _константное_ время _O(1)_; + доступ к элементам по значению за _линейное_ время _O(N)_; + вставка в конец в среднем производится за _константное_ время _O(1)_; + удаление произвольного элемента из списка занимает значительное время т.к. при этом все элементы, находящиеся «правее» смещаются на одну ячейку влево (реальный размер массива (capacity) не изменяется); + вставка элемента в произвольное место списка занимает значительное время т.к. при этом все элементы, находящиеся «правее» смещаются на одну ячейку вправо; + минимум накладных расходов при хранении. `LinkedList`: + на получение элемента по индексу или значению потребуется _линейное_ время _O(N)_; + но доступ к первому и последнему элементу списка всегда осуществляется за _константное_ время _O(1)_ — ссылки постоянно хранятся на первый и последний элемент; + на добавление и удаление в начало или конец списка потребуется _константное_ _O(1)_; + вставка или удаление в/из произвольного место _константное_ _O(1)_; + но поиск позиции вставки и удаления за _линейное_ время _O(N)_; + требует больше памяти для хранения такого же количества элементов, потому что кроме самого элемента хранятся еще указатели на следующий и предыдущий элементы списка. В целом, `LinkedList` в абсолютных величинах проигрывает `ArrayList` и по потребляемой памяти, и по скорости выполнения операций. `LinkedList` предпочтительно применять, когда нужны частые операции вставки/удаления или в случаях, когда необходимо гарантированное время добавления элемента в список. [к оглавлению](#java-collections-framework) ## Что работает быстрее `ArrayList` или `LinkedList`? Смотря какие действия будут выполняться над структурой. см. [Чем отличается `ArrayList` от `LinkedList`](#Чем-отличается-arraylist-от-linkedlist-В-каких-случаях-лучше-использовать-первый-а-в-каких-второй) [к оглавлению](#java-collections-framework) ## Какое худшее время работы метода `contains()` для элемента, который есть в `LinkedList`? _O(N)_. Время поиска элемента линейно пропорционально количеству элементов в списке. [к оглавлению](#java-collections-framework) ## Какое худшее время работы метода `contains()` для элемента, который есть в `ArrayList`? _O(N)_. Время поиска элемента линейно пропорционально количеству элементов с списке. [к оглавлению](#java-collections-framework) ## Какое худшее время работы метода `add()` для `LinkedList`? _O(N)_. Добавление в начало/конец списка осуществляется за время _O(1)_. [к оглавлению](#java-collections-framework) ## Какое худшее время работы метода `add()` для `ArrayList`? _O(N)_. Вставка элемента в конец списка осуществляется за время _O(1)_, но если вместимость массива недостаточна, то происходит создание нового массива с увеличенным размером и копирование всех элементов из старого массива в новый. [к оглавлению](#java-collections-framework) ## Необходимо добавить 1 млн. элементов, какую структуру вы используете? Однозначный ответ можно дать только исходя из информации о том в какую часть списка происходит добавление элементов, что потом будет происходить с элементами списка, существуют ли какие-то ограничения по памяти или скорости выполнения. см. [Чем отличается `ArrayList` от `LinkedList`](#Чем-отличается-arraylist-от-linkedlist-В-каких-случаях-лучше-использовать-первый-а-в-каких-второй) [к оглавлению](#java-collections-framework) ## Как происходит удаление элементов из `ArrayList`? Как меняется в этом случае размер `ArrayList`? При удалении произвольного элемента из списка, все элементы, находящиеся «правее» смещаются на одну ячейку влево и реальный размер массива (его емкость, capacity) не изменяется никак. Механизм автоматического «расширения» массива существует, а вот автоматического «сжатия» нет, можно только явно выполнить «сжатие» командой `trimToSize()`. [к оглавлению](#java-collections-framework) ## Предложите эффективный алгоритм удаления нескольких рядом стоящих элементов из середины списка, реализуемого `ArrayList`. Допустим нужно удалить `n` элементов с позиции `m` в списке. Вместо выполнения удаления одного элемента `n` раз (каждый раз смещая на 1 позицию элементы, стоящие «правее» в списке), нужно выполнить смещение всех элементов, стоящих «правее» `n + m` позиции на `n` элементов «левее» к началу списка. Таким образом, вместо выполнения `n` итераций перемещения элементов списка, все выполняется за 1 проход. Но если говорить об общей эффективности - то самый быстрый способ будет с использованием `System.arraycopy()`, и получить к нему доступ можно через метод - `subList(int fromIndex, int toIndex)` Пример: ```java import java.io.*; import java.util.ArrayList; public class Main { //позиция, с которой удаляем private static int m = 0; //количество удаляемых элементов private static int n = 0; //количество элементов в списке private static final int size = 1000000; //основной список (для удаления вызовом remove() и его копия для удаления путём перезаписи) private static ArrayList initList, copyList; public static void main(String[] args){ initList = new ArrayList<>(size); for (int i = 0; i < size; i++) { initList.add(i); } System.out.println("Список из 1.000.000 элементов заполнен"); copyList = new ArrayList<>(initList); System.out.println("Создана копия списка\n"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); try{ System.out.print("С какой позиции удаляем? > "); m = Integer.parseInt(br.readLine()); System.out.print("Сколько удаляем? > "); n = Integer.parseInt(br.readLine()); } catch(IOException e){ System.err.println(e.toString()); } System.out.println("\nВыполняем удаление вызовом remove()..."); long start = System.currentTimeMillis(); for (int i = m - 1; i < m + n - 1; i++) { initList.remove(i); } long finish = System.currentTimeMillis() - start; System.out.println("Время удаления с помощью вызова remove(): " + finish); System.out.println("Размер исходного списка после удаления: " + initList.size()); System.out.println("\nВыполняем удаление путем перезаписи...\n"); start = System.currentTimeMillis(); removeEfficiently(); finish = System.currentTimeMillis() - start; System.out.println("Время удаления путём смещения: " + finish); System.out.println("Размер копии списка:" + copyList.size()); System.out.println("\nВыполняем удаление через SubList...\n"); start = System.currentTimeMillis(); initList.subList(m - 1, m + n).clear(); finish = System.currentTimeMillis() - start; System.out.println("Время удаления через саблист: " + finish); System.out.println("Размер копии списка:" + copyList.size()); } private static void removeEfficiently(){ /* если необходимо удалить все элементы, начиная с указанного, * то удаляем элементы с конца до m */ if (m + n >= size){ int i = size - 1; while (i != m - 1){ copyList.remove(i); i--; } } else{ //переменная k необходима для отсчёта сдвига начиная от места вставка m for (int i = m + n, k = 0; i < size; i++, k++) { copyList.set(m + k, copyList.get(i)); } /* удаляем ненужные элементы в конце списка * удаляется всегда последний элемент, так как время этого действия * фиксировано и не зависит от размера списка */ int i = size - 1; while (i != size - n - 1){ copyList.remove(i); i--; } //сокращаем длину списка путём удаления пустых ячеек copyList.trimToSize(); } } } ``` Результат выполнения: ``` run: Список из 1.000.000 элементов заполнен Создана копия списка С какой позиции удаляем? > 600000 Сколько удаляем? > 20000 Выполняем удаление вызовом remove()... Время удаления с помощью вызова remove(): 928 Размер исходного списка после удаления: 980000 Выполняем удаление путем перезаписи... Время удаления путём смещения: 17 Размер копии списка:980000 Выполняем удаление через SubList... Время удаления через саблист: 1 Размер копии списка:980000 СБОРКА УСПЕШНО ЗАВЕРШЕНА (общее время: 33 секунды) ``` [к оглавлению](#java-collections-framework) ## Сколько необходимо дополнительной памяти при вызове `ArrayList.add()`? Если в массиве достаточно места для размещения нового элемента, то дополнительной памяти не требуется. Иначе происходит создание нового массива размером в 1,5 раза превышающим существующий (это верно для JDK выше 1.7, в более ранних версиях размер увеличения иной). [к оглавлению](#java-collections-framework) ## Сколько выделяется дополнительно памяти при вызове `LinkedList.add()`? Создается один новый экземпляр вложенного класса `Node`. [к оглавлению](#java-collections-framework) ## Оцените количество памяти на хранение одного примитива типа `byte` в `LinkedList`? Каждый элемент `LinkedList` хранит ссылку на предыдущий элемент, следующий элемент и ссылку на данные. ```java private static class Node { E item; Node next; Node prev; //... } ``` Для 32-битных систем каждая ссылка занимает 32 бита (4 байта). Сам объект (заголовок) вложенного класса `Node` занимает 8 байт. 4 + 4 + 4 + 8 = 20 байт, а т.к. размер каждого объекта в Java кратен 8, соответственно получаем 24 байта. Примитив типа `byte` занимает 1 байт памяти, но в JCF примитивы упаковываются: объект типа `Byte` занимает в памяти 16 байт (8 байт на заголовок объекта, 1 байт на поле типа `byte` и 7 байт для кратности 8). Также напомню, что значения от -128 до 127 кэшируются и для них новые объекты каждый раз не создаются. Таким образом, в x32 JVM 24 байта тратятся на хранение одного элемента в списке и 16 байт - на хранение упакованного объекта типа `Byte`. Итого 40 байт. Для 64-битной JVM каждая ссылка занимает 64 бита (8 байт), размер заголовка каждого объекта составляет 16 байт (два машинных слова). Вычисления аналогичны: 8 + 8 + 8 + 16 = 40байт и 24 байта. Итого 64 байта. [к оглавлению](#java-collections-framework) ## Оцените количество памяти на хранение одного примитива типа `byte` в `ArrayList`? `ArrayList` основан на массиве, для примитивных типов данных осуществляется автоматическая упаковка значения, поэтому 16 байт тратится на хранение упакованного объекта и 4 байта (8 для x64) - на хранение ссылки на этот объект в самой структуре данных. Таким образом, в x32 JVM 4 байта используются на хранение одного элемента и 16 байт - на хранение упакованного объекта типа `Byte`. Для x64 - 8 байт и 24 байта соответственно. [к оглавлению](#java-collections-framework) ## Для `ArrayList` или для `LinkedList` операция добавления элемента в середину (`list.add(list.size()/2, newElement)`) медленнее? Для `ArrayList`: + проверка массива на вместимость. Если вместимости недостаточно, то увеличение размера массива и копирование всех элементов в новый массив (_O(N)_); + копирование всех элементов, расположенных правее от позиции вставки, на одну позицию вправо (_O(N)_); + вставка элемента (_O(1)_). Для `LinkedList`: + поиск позиции вставки (_O(N)_); + вставка элемента (_O(1)_). В худшем случае вставка в середину списка эффективнее для `LinkedList`. В остальных - скорее всего, для `ArrayList`, поскольку копирование элементов осуществляется за счет вызова быстрого системного метода `System.arraycopy()`. [к оглавлению](#java-collections-framework) ## В реализации класса `ArrayList` есть следующие поля: `Object[] elementData`, `int size`. Объясните, зачем хранить отдельно `size`, если всегда можно взять `elementData.length`? Размер массива `elementData` представляет собой вместимость (capacity) `ArrayList`, которая всегда больше переменной `size` - реального количества хранимых элементов. При необходимости вместимость автоматически возрастает. [к оглавлению](#java-collections-framework) ## Сравните интерфейсы `Queue` и `Deque`. ## Кто кого расширяет: `Queue` расширяет `Deque`, или `Deque` расширяет `Queue`? `Queue` - это очередь, которая обычно (но необязательно) строится по принципу FIFO (First-In-First-Out) - соответственно извлечение элемента осуществляется с начала очереди, вставка элемента - в конец очереди. Хотя этот принцип нарушает, к примеру, `PriorityQueue`, использующая «natural ordering» или переданный `Comparator` при вставке нового элемента. `Deque` (Double Ended Queue) расширяет `Queue` и согласно документации, это линейная коллекция, поддерживающая вставку/извлечение элементов с обоих концов. Помимо этого, реализации интерфейса `Deque` могут строится по принципу FIFO, либо LIFO. Реализации и `Deque`, и `Queue` обычно не переопределяют методы `equals()` и `hashCode()`, вместо этого используются унаследованные методы класса Object, основанные на сравнении ссылок. [к оглавлению](#java-collections-framework) ## Почему `LinkedList` реализует и `List`, и `Deque`? `LinkedList` позволяет добавлять элементы в начало и конец списка за константное время, что хорошо согласуется с поведением интерфейса `Deque`. [к оглавлению](#java-collections-framework) ## `LinkedList` — это односвязный, двусвязный или четырехсвязный список? `Двусвязный`: каждый элемент `LinkedList` хранит ссылку на предыдущий и следующий элементы. [к оглавлению](#java-collections-framework) ## Как перебрать элементы `LinkedList` в обратном порядке, не используя медленный `get(index)`? Для этого в `LinkedList` есть обратный итератор, который можно получить вызва метод `descendingIterator()`. [к оглавлению](#java-collections-framework) ## Что позволяет сделать `PriorityQueue`? Особенностью `PriorityQueue` является возможность управления порядком элементов. По-умолчанию, элементы сортируются с использованием «natural ordering», но это поведение может быть переопределено при помощи объекта `Comparator`, который задаётся при создании очереди. Данная коллекция не поддерживает null в качестве элементов. Используя `PriorityQueue`, можно, например, реализовать алгоритм Дейкстры для поиска кратчайшего пути от одной вершины графа к другой. Либо для хранения объектов согласно определённого свойства. [к оглавлению](#java-collections-framework) ## `Stack` считается «устаревшим». Чем его рекомендуют заменять? Почему? `Stack` был добавлен в Java 1.0 как реализация стека LIFO (last-in-first-out) и является расширением коллекции `Vector`, хотя это несколько нарушает понятие стека (например, класс `Vector` предоставляет возможность обращаться к любому элементу по индексу). Является частично синхронизированной коллекцией (кроме метода добавления `push()`) с вытекающими отсюда последствиями в виде негативного воздействия на производительность. После добавления в Java 1.6 интерфейса `Deque`, рекомендуется использовать реализации именно этого интерфейса, например, `ArrayDeque`. [к оглавлению](#java-collections-framework) ## Зачем нужен `HashMap`, если есть `Hashtable`? + Методы класса `Hashtable` синхронизированы, что приводит к снижению производительности, а `HashMap` - нет; + `HashTable` не может содержать элементы `null`, тогда как `HashMap` может содержать один ключ `null` и любое количество значений `null`; + Iterator у `HashMap`, в отличие от Enumeration у `HashTable`, работает по принципу «fail-fast» (выдает исключение при любой несогласованности данных). `Hashtable` это устаревший класс и его использование не рекомендовано. [к оглавлению](#java-collections-framework) ## В чем разница между `HashMap` и `IdentityHashMap`? Для чего нужна `IdentityHashMap`? `IdentityHashMap` - это структура данных, так же реализующая интерфейс `Map` и использующая при сравнении ключей (значений) сравнение ссылок, а не вызов метода `equals()`. Другими словами, в `IdentityHashMap` два ключа `k1` и `k2` будут считаться равными, если они указывают на один объект, т.е. выполняется условие `k1` == `k2`. `IdentityHashMap` не использует метод `hashCode()`, вместо которого применяется метод `System.identityHashCode()`, по этой причине `IdentityHashMap` по сравнению с `HashMap` имеет более высокую производительность, особенно если последний хранит объекты с дорогостоящими методами `equals()` и `hashCode()`. Одним из основных требований к использованию `HashMap` является неизменяемость ключа, а, т.к. `IdentityHashMap` не использует методы `equals()` и `hashCode()`, то это правило на него не распространяется. `IdentityHashMap` может применяться для реализации сериализации/клонирования. При выполнении подобных алгоритмов программе необходимо обслуживать хэш-таблицу со всеми ссылками на объекты, которые уже были обработаны. Такая структура не должна рассматривать уникальные объекты как равные, даже если метод `equals()` возвращает `true`. Пример кода: ```java import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; public class Q2 { public static void main(String[] args) { Q2 q = new Q2(); q.testHashMapAndIdentityHashMap(); } private void testHashMapAndIdentityHashMap() { CreditCard visa = new CreditCard("VISA", "04/12/2019"); Map cardToExpiry = new HashMap<>(); Map cardToExpiryIdenity = new IdentityHashMap<>(); System.out.println("adding to HM"); // inserting objects to HashMap cardToExpiry.put(visa, visa.getExpiryDate()); // inserting objects to IdentityHashMap cardToExpiryIdenity.put(visa, visa.getExpiryDate()); System.out.println("adding to IHM"); System.out.println("before modifying keys"); String result = cardToExpiry.get(visa) != null ? "Yes" : "No"; System.out.println("Does VISA card exists in HashMap? " + result); result = cardToExpiryIdenity.get(visa) != null ? "Yes" : "No"; System.out.println("Does VISA card exists in IdenityHashMap? " + result); // modifying value object visa.setExpiryDate("02/11/2030"); System.out.println("after modifying keys"); result = cardToExpiry.get(visa) != null ? "Yes" : "No"; System.out.println("Does VISA card exists in HashMap? " + result); result = cardToExpiryIdenity.get(visa) != null ? "Yes" : "No"; System.out.println("Does VISA card exists in IdenityHashMap? " + result); System.out.println("cardToExpiry.containsKey"); System.out.println(cardToExpiry.containsKey(visa)); System.out.println("cardToExpiryIdenity.containsKey"); System.out.println(cardToExpiryIdenity.containsKey(visa)); } } class CreditCard { private String issuer; private String expiryDate; public CreditCard(String issuer, String expiryDate) { this.issuer = issuer; this.expiryDate = expiryDate; } public String getIssuer() { return issuer; } public String getExpiryDate() { return expiryDate; } public void setExpiryDate(String expiry) { this.expiryDate = expiry; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((expiryDate == null) ? 0 : expiryDate.hashCode()); result = prime * result + ((issuer == null) ? 0 : issuer.hashCode()); System.out.println("hashCode = " + result); return result; } @Override public boolean equals(Object obj) { System.out.println("equals !!! "); if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; CreditCard other = (CreditCard) obj; if (expiryDate == null) { if (other.expiryDate != null) return false; } else if (!expiryDate.equals(other.expiryDate)) return false; if (issuer == null) { if (other.issuer != null) return false; } else if (!issuer.equals(other.issuer)) return false; return true; } } ``` Результат выполнения кода: ``` adding to HM hashCode = 1285631513 adding to IHM before modifying keys hashCode = 1285631513 Does VISA card exists in HashMap? Yes Does VISA card exists in IdenityHashMap? Yes after modifying keys hashCode = 791156485 Does VISA card exists in HashMap? No Does VISA card exists in IdenityHashMap? Yes cardToExpiry.containsKey hashCode = 791156485 false cardToExpiryIdenity.containsKey true ``` [к оглавлению](#java-collections-framework) ## В чем разница между `HashMap` и `WeakHashMap`? Для чего используется `WeakHashMap`? В Java существует 4 типа ссылок: _сильные (strong reference)_, _мягкие (SoftReference)_, _слабые (WeakReference)_ и _фантомные (PhantomReference)_. Особенности каждого типа ссылок связаны с работой Garbage Collector. Если объект можно достичь только с помощью цепочки WeakReference (то есть на него отсутствуют сильные и мягкие ссылки), то данный объект будет помечен на удаление. `WeakHashMap` - это структура данных, реализующая интерфейс `Map` и основанная на использовании WeakReference для хранения ключей. Таким образом, пара «ключ-значение» будет удалена из `WeakHashMap`, если на объект-ключ более не имеется сильных ссылок. В качестве примера использования такой структуры данных можно привести следующую ситуацию: допустим имеются объекты, которые необходимо расширить дополнительной информацией, при этом изменение класса этих объектов нежелательно либо невозможно. В этом случае добавляем каждый объект в `WeakHashMap` в качестве ключа, а в качестве значения - нужную информацию. Таким образом, пока на объект имеется сильная ссылка (либо мягкая), можно проверять хэш-таблицу и извлекать информацию. Как только объект будет удален, то WeakReference для этого ключа будет помещен в ReferenceQueue и затем соответствующая запись для этой слабой ссылки будет удалена из `WeakHashMap`. [к оглавлению](#java-collections-framework) ## В `WeakHashMap` используются WeakReferences. А почему бы не создать `SoftHashMap` на SoftReferences? `SoftHashMap` представлена в сторонних библиотеках, например, в `Apache Commons`. [к оглавлению](#java-collections-framework) ## В `WeakHashMap` используются WeakReferences. А почему бы не создать `PhantomHashMap` на PhantomReferences? PhantomReference при вызове метода `get()` возвращает всегда `null`, поэтому тяжело представить назначение такой структуры данных. [к оглавлению](#java-collections-framework) ## `LinkedHashMap` - что в нем от `LinkedList`, а что от `HashMap`? Реализация `LinkedHashMap` отличается от `HashMap` поддержкой двухсвязанного списка, определяющего порядок итерации по элементам структуры данных. По умолчанию элементы списка упорядочены согласно их порядку добавления в `LinkedHashMap` (insertion-order). Однако порядок итерации можно изменить, установив параметр конструктора `accessOrder` в значение `true`. В этом случае доступ осуществляется по порядку последнего обращения к элементу (access-order). Это означает, что при вызове методов `get()` или `put()` элемент, к которому обращаемся, перемещается в конец списка. При добавлении элемента, который уже присутствует в `LinkedHashMap` (т.е. с одинаковым ключом), порядок итерации по элементам не изменяется. [к оглавлению](#java-collections-framework) ## В чем проявляется «сортированность» `SortedMap`, кроме того, что `toString()` выводит все элементы по порядку? Так же оно проявляется при итерации по коллекции. [к оглавлению](#java-collections-framework) ## Как устроен `HashMap`? `HashMap` состоит из «корзин» (bucket). С технической точки зрения «корзины» — это элементы массива, которые хранят ссылки на списки элементов. При добавлении новой пары «ключ-значение», вычисляет хэш-код ключа, на основании которого вычисляется номер корзины (номер ячейки массива), в которую попадет новый элемент. Если корзина пустая, то в нее сохраняется ссылка на вновь добавляемый элемент, если же там уже есть элемент, то происходит последовательный переход по ссылкам между элементами в цепочке, в поисках последнего элемента, от которого и ставится ссылка на вновь добавленный элемент. Если в списке был найден элемент с таким же ключом, то он заменяется. [к оглавлению](#java-collections-framework) ## Согласно Кнуту и Кормену существует две основных реализации хэш-таблицы: на основе открытой адресации и на основе метода цепочек. Как реализована `HashMap`? Почему, по вашему мнению, была выбрана именно эта реализация? В чем плюсы и минусы каждого подхода? `HashMap` реализован с использованием метода цепочек, т.е. каждой ячейке массива (корзине) соответствует свой связный список и при возникновении коллизии осуществляется добавление нового элемента в этот список. Для метода цепочек коэффициент заполнения может быть больше 1 и с увеличением числа элементов производительность убывает линейно. Такие таблицы удобно использовать, если заранее неизвестно количество хранимых элементов, либо их может быть достаточно много, что приводит к большим значениям коэффициента заполнения. Среди методов открытой реализации различают: + линейное пробирование; + квадратичное пробирование; + двойное хэширование. Недостатки структур с методом открытой адресации: + Количество элементов в хэш-таблице не может превышать размера массива. По мере увеличения числа элементов и повышения коэффициента заполнения производительность структуры резко падает, поэтому необходимо проводить перехэширование. + Сложно организовать удаление элемента. + Первые два метода открытой адресации приводят к проблеме первичной и вторичной группировок. Преимущества хэш-таблицы с открытой адресацией: + отсутствие затрат на создание и хранение объектов списка; + простота организации сериализации/десериализации объекта. [к оглавлению](#java-collections-framework) ## Как работает `HashMap` при попытке сохранить в него два элемента по ключам с одинаковым `hashCode()`, но для которых `equals() == false`? По значению `hashCode()` вычисляется индекс ячейки массива, в список которой этот элемент будет добавлен. Перед добавлением осуществляется проверка на наличие элементов в этой ячейке. Если элементы с таким `hashCode()` уже присутствует, но их `equals()` методы не равны, то элемент будет добавлен в конец списка. [к оглавлению](#java-collections-framework) ## Какое начальное количество корзин в `HashMap`? В конструкторе по умолчанию - 16, используя конструкторы с параметрами можно задавать произвольное начальное количество корзин. [к оглавлению](#java-collections-framework) ## Какова оценка временной сложности операций над элементами из `HashMap`? Гарантирует ли `HashMap` указанную сложность выборки элемента? В общем случае операции добавления, поиска и удаления элементов занимают константное время. Данная сложность не гарантируется, т.к. если хэш-функция распределяет элементы по корзинам равномерно, временная сложность станет не хуже [_Логарифмического времени_ ](https://ru.wikipedia.org/wiki/%D0%92%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F_%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D0%B0#%D0%9B%D0%BE%D0%B3%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%B2%D1%80%D0%B5%D0%BC%D1%8F) O(log(N)), а в случае, когда хэш-функция постоянно возвращает одно и то же значение, `HashMap` превратится в связный список со сложностью О(n). Пример кода двоичного поиска: ```java public class Q { public static void main(String[] args) { Q q = new Q(); q.binSearch(); } private void binSearch() { int[] inpArr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; Integer result = binSearchF(inpArr, 1, 0, inpArr.length - 1); System.out.println("-----------------------"); result = binSearchF(inpArr, 2, 0, inpArr.length - 1); System.out.println("Found at position " + result); } private Integer binSearchF(int[] inpArr, int searchValue, int low, int high) { Integer index = null; while (low <= high) { System.out.println("New iteration, low = " + low + ", high = " + high); int mid = (low + high) / 2; System.out.println("trying mid = " + mid + " inpArr[mid] = " + inpArr[mid]); if (inpArr[mid] < searchValue) { low = mid + 1; System.out.println("inpArr[mid] (" + inpArr[mid] + ") < searchValue(" + searchValue + "), mid = " + mid + ", setting low = " + low); } else if (inpArr[mid] > searchValue) { high = mid - 1; System.out.println("inpArr[mid] (" + inpArr[mid] + ") > searchValue(" + searchValue + "), mid = " + mid + ", setting high = " + high); } else if (inpArr[mid] == searchValue) { index = mid; System.out.println("found at index " + mid); break; } } return index; } } ``` [к оглавлению](#java-collections-framework) ## Возможна ли ситуация, когда `HashMap` выродится в список даже с ключами имеющими разные `hashCode()`? Это возможно в случае, если метод, определяющий номер корзины будет возвращать одинаковые значения. [к оглавлению](#java-collections-framework) ## В каком случае может быть потерян элемент в `HashMap`? Допустим, в качестве ключа используется не примитив, а объект с несколькими полями. После добавления элемента в `HashMap` у объекта, который выступает в качестве ключа, изменяют одно поле, которое участвует в вычислении хэш-кода. В результате при попытке найти данный элемент по исходному ключу, будет происходить обращение к правильной корзине, а вот `equals` уже не найдет указанный ключ в списке элементов. Тем не менее, даже если `equals` реализован таким образом, что изменение данного поля объекта не влияет на результат, то после увеличения размера корзин и пересчета хэш-кодов элементов, указанный элемент, с измененным значением поля, с большой долей вероятности попадет в совершенно другую корзину и тогда уже потеряется совсем. [к оглавлению](#java-collections-framework) ## Почему нельзя использовать `byte[]` в качестве ключа в `HashMap`? Хэш-код массива не зависит от хранимых в нем элементов, а присваивается при создании массива (метод вычисления хэш-кода массива не переопределен и вычисляется по стандартному `Object.hashCode()` на основании адреса массива). Так же у массивов не переопределен `equals` и выполняется сравнение указателей. Это приводит к тому, что обратиться к сохраненному с ключом-массивом элементу не получится при использовании другого массива такого же размера и с такими же элементами, доступ можно осуществить лишь в одном случае — при использовании той же самой ссылки на массив, что использовалась для сохранения элемента. [к оглавлению](#java-collections-framework) ## Какова роль `equals()` и `hashCode()` в `HashMap`? `hashCode` позволяет определить корзину для поиска элемента, а `equals` используется для сравнения ключей элементов в списке корзины и искомого ключа. [к оглавлению](#java-collections-framework) ## Каково максимальное число значений `hashCode()`? Число значений следует из сигнатуры `int hashCode()` и равно диапазону типа `int` — __232__. [к оглавлению](#java-collections-framework) ## Какое худшее время работы метода get(key) для ключа, которого нет в `HashMap`? ## Какое худшее время работы метода get(key) для ключа, который есть в `HashMap`? ___O(N)___. Худший случай - это поиск ключа в `HashMap`, вырожденного в список по причине совпадения ключей по `hashCode()` и для выяснения хранится ли элемент с определённым ключом может потребоваться перебор всего списка. Но начиная с Java 8, после определенного числа элементов в списке, связный список преобразовывается в красно-черное дерево и сложность выборки, даже в случае плохой хеш-функции, не хуже _логарифмической_ _O(log(N))_ [к оглавлению](#java-collections-framework) ## Сколько переходов происходит в момент вызова `HashMap.get(key)` по ключу, который есть в таблице? + ключ равен `null`: __1__ - выполняется единственный метод `getForNullKey()`. + любой ключ отличный от `null`: __4__ - вычисление хэш-кода ключа; определение номера корзины; поиск значения; возврат значения. [к оглавлению](#java-collections-framework) ## Сколько создается новых объектов, когда вы добавляете новый элемент в `HashMap`? __Один__ новый объект статического вложенного класса `Entry`. [к оглавлению](#java-collections-framework) ## Как и когда происходит увеличение количества корзин в `HashMap`? Помимо `capacity` у `HashMap` есть еще поле `loadFactor`, на основании которого, вычисляется предельное количество занятых корзин `capacity * loadFactor`. По умолчанию `loadFactor = 0.75`. По достижению предельного значения, число корзин увеличивается в 2 раза и для всех хранимых элементов вычисляется новое «местоположение» с учетом нового числа корзин. [к оглавлению](#java-collections-framework) ## Объясните смысл параметров в конструкторе `HashMap(int initialCapacity, float loadFactor)`. + `initialCapacity` - исходный размер `HashMap`, количество корзин в хэш-таблице в момент её создания. + `loadFactor` - коэффициент заполнения `HashMap`, при превышении которого происходит увеличение количества корзин и автоматическое перехэширование. Равен отношению числа уже хранимых элементов в таблице к её размеру. [к оглавлению](#java-collections-framework) ## Будет ли работать `HashMap`, если все добавляемые ключи будут иметь одинаковый `hashCode()`? Да, будет, но в этом случае `HashMap` вырождается в связный список и теряет свои преимущества. ## Как перебрать все ключи `Map`? Использовать метод `keySet()`, который возвращает множество `Set` ключей. [к оглавлению](#java-collections-framework) ## Как перебрать все значения `Map`? Использовать метод `values()`, который возвращает коллекцию `Collection` значений. [к оглавлению](#java-collections-framework) ## Как перебрать все пары «ключ-значение» в `Map`? Использовать метод `entrySet()`, который возвращает множество `Set` пар «ключ-значение». [к оглавлению](#java-collections-framework) ## В чем отличия `TreeSet` и `HashSet`? `TreeSet` обеспечивает упорядоченно хранение элементов в виде красно-черного дерева. Сложность выполнения основных операций не хуже _O(log(N))_ (_Логарифмическое время_). `HashSet` использует для хранения элементов такой же подход, что и `HashMap`, за тем отличием, что в `HashSet` в качестве ключа и значения выступает сам `элемент`, кроме того, `HashSet` не поддерживает упорядоченное хранение элементов и обеспечивает временную сложность выполнения операций аналогично `HashMap`. [к оглавлению](#java-collections-framework) ## Что будет, если добавлять элементы в `TreeSet` по возрастанию? В основе `TreeSet` лежит красно-черное дерево, которое умеет само себя балансировать. В итоге, `TreeSet` все равно в каком порядке вы добавляете в него элементы, преимущества этой структуры данных будут сохраняться. [к оглавлению](#java-collections-framework) ## Чем `LinkedHashSet` отличается от `HashSet`? `LinkedHashSet` отличается от `HashSet` только тем, что в его основе лежит `LinkedHashMap` вместо `HashMap`. Благодаря этому порядок элементов при обходе коллекции является идентичным порядку добавления элементов (insertion-order). При добавлении элемента, который уже присутствует в `LinkedHashSet` (т.е. с одинаковым ключом), порядок обхода элементов не изменяется. [к оглавлению](#java-collections-framework) ## Для `Enum` есть специальный класс `java.util.EnumSet`. Зачем? Чем авторов не устраивал `HashSet` или `TreeSet`? `EnumSet` - это реализация интерфейса `Set` для использования с перечислениями (`Enum`). В структуре данных хранятся объекты только одного типа `Enum`, указываемого при создании. Для хранения значений `EnumSet` использует массив битов (_bit vector_), - это позволяет получить высокую компактность и эффективность. Проход по `EnumSet` осуществляется согласно порядку объявления элементов перечисления. Все основные операции выполняются за _O(1)_ и обычно (но негарантированно) быстрей аналогов из `HashSet`, а пакетные операции (_bulk operations_), такие как `containsAll()` и `retainAll()` выполняются даже гораздо быстрей. Помимо всего `EnumSet` предоставляет множество статических методов инициализации для упрощенного и удобного создания экземпляров. [к оглавлению](#java-collections-framework) ## Какие существуют способы перебирать элементы списка? + Цикл с итератором ```java Iterator iterator = list.iterator(); while (iterator.hasNext()) { //iterator.next(); } ``` + Цикл `for` ```java for (int i = 0; i < list.size(); i++) { //list.get(i); } ``` + Цикл `while` ```java int i = 0; while (i < list.size()) { //list.get(i); i++; } ``` + «for-each» ```java for (String element : list) { //element; } ``` [к оглавлению](#java-collections-framework) ## Каким образом можно получить синхронизированные объекты стандартных коллекций? С помощью статических методов `synchronizedMap()` и `synchronizedList()` класса `Collections`. Данные методы возвращают синхронизированный декоратор переданной коллекции. При этом все равно в случае обхода по коллекции требуется ручная синхронизация. ```java Map m = Collections.synchronizedMap(new HashMap()); List l = Collections.synchronizedList(new ArrayList()); ``` Начиная с Java 6 JCF был расширен специальными коллекциями, поддерживающими многопоточный доступ, такими как `CopyOnWriteArrayList` и `ConcurrentHashMap`. [к оглавлению](#java-collections-framework) ## Как получить коллекцию только для чтения? При помощи: + `Collections.unmodifiableList(list)`; + `Collections.unmodifiableSet(set)`; + `Collections.unmodifiableMap(map)`. Эти методы принимают коллекцию в качестве параметра, и возвращают коллекцию только для чтения с теми же элементами внутри. [к оглавлению](#java-collections-framework) ## Напишите однопоточную программу, которая заставляет коллекцию выбросить `ConcurrentModificationException`. ```java public static void main(String[] args) { List list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); for (Integer integer : list) { list.remove(1); } } ``` [к оглавлению](#java-collections-framework) ## Приведите пример, когда какая-либо коллекция выбрасывает `UnsupportedOperationException`. ```java public static void main(String[] args) { List list = Collections.emptyList(); list.add(0); } ``` [к оглавлению](#java-collections-framework) ## Реализуйте симметрическую разность двух коллекций используя методы `Collection` (`addAll(...)`, `removeAll(...)`, `retainAll(...)`). Симметрическая разность двух коллекций - это множество элементов, одновременно не принадлежащих обоим исходным коллекциям. ```java Collection symmetricDifference(Collection a, Collection b) { // Объединяем коллекции. Collection result = new ArrayList<>(a); result.addAll(b); // Получаем пересечение коллекций. Collection intersection = new ArrayList<>(a); intersection.retainAll(b); // Удаляем элементы, расположенные в обоих коллекциях. result.removeAll(intersection); return result; } ``` [к оглавлению](#java-collections-framework) ## Как, используя LinkedHashMap, сделать кэш c «invalidation policy»? Необходимо использовать _LRU-алгоритм (Least Recently Used algorithm)_ и `LinkedHashMap` с access-order. В этом случае при обращении к элементу он будет перемещаться в конец списка, а наименее используемые элементы будут постепенно группироваться в начале списка. Так же в стандартной реализации `LinkedHashMap` есть метод `removeEldestEntries()`, который возвращает `true`, если текущий объект `LinkedHashMap` должен удалить наименее используемый элемент из коллекции при использовании методов `put()` и `putAll()`. ```java public class LRUCache extends LinkedHashMap { private static final int MAX_ENTRIES = 10; public LRUCache(int initialCapacity) { super(initialCapacity, 0.85f, true); } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } } ``` Стоит заметить, что `LinkedHashMap` не позволяет полностью реализовать LRU-алгоритм, поскольку при вставке уже имеющегося в коллекции элемента порядок итерации по элементам не меняется. [к оглавлению](#java-collections-framework) ## Как одной строчкой скопировать элементы любой `collection` в массив? ```java Object[] array = collection.toArray(); ``` [к оглавлению](#java-collections-framework) ## Как одним вызовом из `List` получить `List` со всеми элементами, кроме первых и последних 3-х? ```java List subList = list.subList(3, list.size() - 3); ``` [к оглавлению](#java-collections-framework) ## Как одной строчкой преобразовать `HashSet` в `ArrayList`? ```java ArrayList list = new ArrayList<>(new HashSet<>()); ``` [к оглавлению](#java-collections-framework) ## Как одной строчкой преобразовать `ArrayList` в `HashSet`? ```java HashSet set = new HashSet<>(new ArrayList<>()); ``` [к оглавлению](#java-collections-framework) ## Сделайте `HashSet` из ключей `HashMap`. ```java HashSet set = new HashSet<>(map.keySet()); ``` [к оглавлению](#java-collections-framework) ## Сделайте `HashMap` из `HashSet>`. ```java HashMap map = new HashMap<>(set.size()); for (Map.Entry entry : set) { map.put(entry.getKey(), entry.getValue()); } ``` [к оглавлению](#java-collections-framework) # Источник + [parshinpn.pro](http://www.parshinpn.pro/content/voprosy-i-otvety-na-sobesedovanii-po-teme-java-collection-framework-chast-1) + [Хабрахабр](https://habrahabr.ru/post/162017/) + [Quizful](http://www.quizful.net/interview/java) + [JavaRush](http://info.javarush.ru/) + [Хабрахабр:Справочник по Java Collections Framework](https://habrahabr.ru/post/237043/) [Вопросы для собеседования](README.md)