• ,

Часто задаваемые на собеседованиях вопросы по классам коллекциям в Java (Часть 1).

Без сомнения, коллекции в Java это крайне важная область, и вопросы по коллекциям будут задавать на собеседованиях как новичкам так и опытным программистам. Тема настолько обширна, что практически невозможно покрыть ее целиком. И все же, основываясь на моих предыдущих собеседованиях, я попробую перечислить как можно больше ХОРОШИХ вопросов, к которым вы должны быть готовы.
Вопросы будут как сложные так и простые, так что если вопрос кажется вам слишком примитивным — не забывайте что он отлично подойдет менее опытному программисту.
Вопросы в этой статье:
    Общие вопросы
  1. Что такое коллекции в Java? Перечислите их преимущества
  2. Расскажите про иерархию коллекций
  3. Почему коллекции не наследуют интерфейсы Cloneable и Serializable?
  4. Почему интерфейс Map не наследует интерфейс Collection?
  5. Вопросы про списки
  6. Почему мы используем списки? Какие основные классы реализуют интерфейс List?
  7. Как преобразовать массив строк в ArrayList?
  8. Как отсортировать список в обратном порядке?
  9. Вопросы про множества
  10. Почему мы используем множества? Какие основные классы реализуют интерфейс Set?
  11. Как хранятся элементы в HashSet?
  12. Может ли элемент null быть добавлен в TreeSet или HashSet?
  13. Вопросы про словари
  14. Почему мы используем словари? Какие основные классы реализуют интерфейс Map?
  15. Что такое IdentityHashMap и WeakHashMap?
  16. Объясните что такое ConcurrentHashMap? Как оно работает?
  17. Как работают словари?
  18. Как создать хороший ключ для словаря?
  19. Какие представления содержимого предоставляет интерфейс Map?
  20. Когда нужно использовать HashMap, а когда TreeMap?
  21. Вопросы о различиях разных коллекций
  22. Назовите различия между Set и List?
  23. Назовите различия между List и Map?
  24. Назовите различия между HashMap и HashTable?
  25. Назовите различия между Vector и ArrayList?
  26. Назовите различия между Iterator и Enumeration?
  27. Назовите различия между HashMap и HashSet?
  28. Назовите различия между Iterator и ListIterator?
  29. Назовите различия между TreeSet и SortedSet?
  30. Назовите различия между ArrayList и LinkedList?
  31. И еще вопросы
  32. Как сделать коллекцию только для чтения?
  33. Как сделать потокобезопасную коллекцию?
  34. Почему не существует метода Iterator.add() для добавления элементов в коллекцию?
  35. Какие существуют способы перебирать элементы списка?
  36. Как вы понимаете работу свойства итератора fail-fast?
  37. Какая разница между fail-fast и fail-safe?
  38. Как избежать ConcurrentModificationException во время перебора коллекции?
  39. Что такое UnsupportedOperationException?
  40. Какие классы коллекций дают доступ к любому элементу?
  41. Что такое BlockingQueue?
  42. Что такое очередь и стэк, перечислите различия между ними?
  43. Что такое интерфейсы Comparable и Comparator?
  44. Что такое классы Collections и Arrays?
  45. Список использованной литературы

Не тратя зря время, приступим к объяснениям

    Общие вопросы
  1. Что такое коллекции в Java? Перечислите их преимущества?
    По определению — коллекция это объект представляющий собой группу объектов. Как в теории множеств — множество это группа объектов. Просто, не так ли? До выхода JDK 1.2, существовали классы такие как Vector и HashTable, но не было фреймворка Collection. Потом было решено добавить поддержку многократно используемых структур данных. Данный фреймворк был разработан преимущественно Джошуа Блохом, и впервые появился в JDK 1.2.
      В качестве главных преимуществ мы можем перечислить:
    • Уменьшаются затраты времени на написание кода
    • Улучшается производительность, благодаря использованию высокоэффективных алгоритмов и структур данных
    • Коллекции являются универсальным способом хранения и передачи данных, что упрощает взаимодействие разных частей кода
    • Простота в изучении, потому что необходимо выучить только самые верхние интерфейсы и поддерживаемые операции
  2. Расскажите про иерархию коллекций?

    Как показано на картинке, фреймворк коллекций содержит один интерфейс верхнего уровня — Collection, от которого наследуются Set, List и Queue. Ниже мы рассмотрим еще множество классов, содержащихся в этих трех ветвях.
    Запомните заголовок интерфейса Collection, это поможет вам с многими вопросами.
    
    public interface Collection extends Iterable {
    //описание методов
    }
    

    Также фреймворк содержит интерфейс Map, который не является наследником интерфейса Collection. Причину почему он не наследует Collection, мы разберем в четвертом вопросе.
  3. Почему коллекции не наследуют интерфейсы Cloneable и Serializable?
    Ну, простейший ответ — «потому что не надо». Функционал предоставляемый интерфейсами Cloneable и Serializable просто не нужен для коллекций.

    Еще одна причина — далеко не всегда нужен подкласс Cloneable потому что каждая операция клонирования потребляет очень много памяти, и неопытные программисты могут расходовать ее сами не понимая последствий.

    И последняя причина — клонирование и сериализация являются очень узкоспецифичными операциями, и реализовывать их нужно только когда это необходимо. Многие классы коллекции реализуют данные интерфейсы, но совершенно незачем закладывать их для всех коллекций вообще. Если вам нужно клонирование и сериализация — просто воспользуйтесь теми классами где она есть, если нет — остальными классами.
  4. Почему интерфейс Map не наследует интерфейс Collection?
    Хороший ответ на этот вопрос — «потому что они несовместимы». В интерфейсе Collection описан метод add(Object o). Словари не могут содержать этот метод, потому что работают с парами ключ/значение. Также, словари имеют представления keySet, valueSet, которых нет в коллекциях.

    В связи с этими различиями, интерфейс Map не может наследовать интерфейс Collection, и представляет собой отдельную ветвь иерархии.
  5. Вопросы про списки
  6. Почему мы используем списки? Какие основные классы реализуют интерфейс List?
    Списки в Java это упорядоченная коллекция элементов. Каждый элемент имеет индекс, начинающийся с нуля. Все индексы уникальны. Кроме методов описанных в интерфейсе Collection, списки имеют свои собственные методы, в основном для работы с элементами коллекциями по их индексу. Можно разделить эти методы на 3 группы — поиск элемента, получение конкретного элемента, перебор коллекции и выборка подгруппы. Все эти операции могут производиться по индексу элемента.

    Основные классы, реализующие интерфейс List это Stack, Vector, ArrayList и LinkedList. За более подробной информацией по ним, обратитесь к документации.
  7. Как преобразовать массив строк в ArrayList?
    Вопрос этот несколько глубже чем просто по программированию, как это видится новичкам. Цель его — проверить знание кандидатом служебных классов фреймворка Collection. Рассмотрим два таких класса, наиболее востребованных на собеседованиях — Collections и Arrays.

    Класс Collections предоставляет статические методы для операций над коллекциями. Соответственно Arrays предоставляет статические методы для операций над массивами.
    String[] words = {"аз", "буки", "веди", "глагол", "добро"};
    //Как вы можете обратить внимание, у нас есть массив строк String[] words. 
    //В котором у нас лежат 5 строк.
    List wordList = Arrays.asList(words);
    //легким движением руки, а точнее вызовом Arrays.asList() мы превратили наш
    //массив строк в список List wordList.

    Также хотелось бы отметить, что этот метод способен обрабатывать не только строки, он создаст список элементов любого типа, которого был массив.
    Integer[] nums = {1, 2, 3, 4};
    List numList = Arrays.asList(nums);
  8. Как отсортировать список в обратном порядке?
    Как и предыдущий, этот вопрос проверяет ваше знание служебных классов Collection
    List reversedList = Collections.reverse(list);
  9. Вопросы про множества
  10. Почему мы используем множества? Какие основные классы реализуют интерфейс Set?
    Он моделирует математическое множество, из теории множеств. Интерфейс Set похож на List, но имеет некоторые отличия. Первое — это не упорядоченная коллекция. Следовательно, добавление/удаление элементов не требует их сортировки. Главная особенность множеств — уникальность элементов, то есть один и тот же элемент не может содержаться в множестве дважды.

    Очень важными для функционирования множеств являются методы equals() и hashCode(), они позволяют сравнивать множества разных классов. Два множества являются идентичными только если они содержат одни и те же элементы.

    Как следует из вышеизложенного, множества не поддерживают операций основанных на индексе элемента, как списки. Множества имеют только те методы которые описаны в интерфейсе Collection.

    Основными классами, реализующими интерфейс Set, являются EnumSet, HashSet, LinkedHashSet и TreeSet. Если хотите узнать больше — почитайте соответствующие разделы документации Java.
  11. Как хранятся элементы в HashSet?
    Как вы уже в курсе, HashMap хранит пары ключ/значение, и ключи должны быть уникальны. HashSet использует эту особенность HashMap для обеспечения уникальности своих элементов. В классе HashSet, словарь описан следующим образом:
    private transient HashMap<E, Object> map;
    private static final Object PRESENT = new Object();
    

    Итак, когда вы сохраняете элемент в множестве, оно кладет данный элемент в качестве ключа в словарь, а значением идет объект PRESENT, как это описано ниже:
    public boolean add(E e) {
    	return map.put(e, PRESENT) == null;
    }

    Я настоятельно рекомендую вам прочесть эту статью, это поможет вам с легкостью ответить на все связанные с HashMap вопросы.
  12. Может ли элемент null быть добавлен в TreeSet или HashSet?
    Как видно из предыдущего ответа, в методе add() нет проверки на null. Также, HashMap позволяет один ключ null, следовательно, один элемент null может быть добавлен в HashSet.

    TreeSet работает по тому же принципу что и HashSet, но использует NavigableMap для хранения элементов
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    

    NavigableMap это класс-наследник SortedMap, а SortedMap не позволяет использование ключей null. Следовательно, и TreeMap не поддерживает хранение элементов типа null. Если вы попробуете добавить null в TreeSet, получите исключение NullPointerException.
  13. Вопросы про словари
  14. Почему мы используем словари (Map)? Какие основные классы реализуют интерфейс Map?
    Словари — специальный тип коллекции, которая используется для хранения пар ключ/значение. По этой причине он не является наследником интерфейса Collection. Словарь предоставляет методы для добавления пар ключ/значение, удаления, поиска и перебора по предоставляемым словарем представлениям данных.
    Основные классы реализующие интерфейс Map: HashMap, Hashtable, EnumMap, IdentityHashMap, LinkedHashMap и Properties.
  15. Что такое IdentityHashMap и WeakHashMap?
    IdentityHashMap похож на HashMap за одним исключением — для сравнения объектов используется сравнение указателей на объекты, если указатели не равны (указывают на объекты лежащие по разным адресам), значит объекты считаются различными. IdentityHashMap является довольно редко используемым. Хотя он реализует интерфейс Map, он нарушает один из основных принципов устройства Map, который требует использования метода equals() для сравнения объектов. IdentityHashMap используется только в тех случаях, когда требуется сравнение объектов по их адресам.

    WeakHashMap это реализация интерфейса Map, которая содержит слабые ссылки на элементы. То есть, если за пределами WeakHashMap не осталось ни одной ссылки на его элемент, этот элемент удаляется сборщиком мусора. Класс предназначен для использования с объектами, у которых метод equals() проверяет идентичность объектов с помощью оператора ==. После того как элемент будет удален сборщиком мусора, он уже не может быть восстановлен, и к большому удивлению программиста найти его в словаре больше не получится.
  16. Объясните что такое ConcurrentHashMap? Как оно работает?
    Взято с официальной документации:
    Реализация словаря полностью поддерживающая многопоточное добавление/удаление/поиск элементов. Данный класс следует тем же спецификациям что и Hashtable, и содержит методы соответствующие методам Hashtable. Однако, хотя все операции являются потокобезопасными, операция по выборке элементов не блокирует таблицу, и вообще нет возможности запретить весь доступ к таблице. Этот класс совместим с Hashtable во всем кроме вопросов многопоточной синхронизации.
  17. Как работает hashmap?
    Самый важный вопрос, который скорее всего будет задан на собеседовании программисту любого уровня. Вы должны хорошо разбираться в этой теме, и не только потому что это самый задаваемый вопрос, но и потому что понимание устройства hashmap позволяет вам легче разобраться в других особенностях работы коллекций.

    Ответ на этот вопрос очень обширный, и полностью его можно прочесть в этой статье — как работает hashmap. А на данный момент просто запомните что HashMap работает на основе хэширования. Словарь, по определению, это объект который связывает ключи и значения. Для хранения таких структур, он использует внутренний класс Entry.
    static class Entry implements Map.Entry
    {
    final K key;
    V value;
    Entry next;
    final int hash;
    ...//Еще много кода тут
    }

    Переменные key и value служат для хранения ключа и значения. А сами объекты Entry лежат в массиве
    /**
    * Размер таблицы меняется по необходимости, 
    * и обязательно должен быть равен степени двойки
    */
    transient Entry[] table;

    Индекс нужного элемента в массиве вычисляется по хэш-коду ключа. Больше информации можете получить по ссылке в начале ответа.
  18. Как создать хороший ключ для словаря?
    Следующий хороший вопрос, который обычно задают следом за вопросом о функционировании HashMap. Итак, главное ограничение — ключ должен быть таким, чтобы потом по нему можно было получить из словаря значение. Иначе в его использовании просто нет смысла. Если вы понимаете как функционирует hashmap, вы знаете что его работа сильно зависит от методов hashCode() и equals() объектов-ключей.

    Как следует из вышеизложенного, хороший ключ должен давать один и тот же hashCode снова и снова, независимо от того сколько раз он запрашивается. А также, одинаковые ключи, при вызове метода equals() должны возвращать true, а разные — false.

    Из чего следует, что лучшими кандидатами на роль ключа являются неизменяемые классы.

    Можете почитать еще по адресу.
  19. Какие представления содержимого предоставляет интерфейс Map?
    Интерфейс Map предоставляет три представления хранящихся данных:
    • множество всех ключей
    • множество всех значений
    • множество объектов Entry, содержащих в себе и ключ и значение
    Перемещаться по ним можно с помощью итераторов.
  20. Когда нужно использовать HashMap, а когда TreeMap?
    HashMap это очень широко используемый класс, и вы это знаете. Так что, я ограничусь тем, что скажу что в нем хранятся пары ключ/значение и он позволяет проводить над ними многие операции.

    TreeMap это особая разновидность HashMap. Разница в том, что ключи в TreeMap хранятся упорядоченно. По умолчанию применяется «естественная сортировка». Переопределить сортировку можно предоставив экземпляр класса Comparator, метод compare которого и будет использован для сортировки ключей.

    Обратите внимание, что все ключи добавленные в словарь должны реализовывать интерфейс Comparable (это необходимо для сортировки). Более того, все ключи должны быть взаимно совместимыми:k1.compareTo(k2) не должен вызывать ClassCastException для любых k1 и k2 хранящихся в словаре. Если пользователь попытается положить в словарь ключ который нарушает это условие (к примеру, строковый ключ в словарь где все ключи типа Integer), метод put(Object key, Object value) должен вызвать ClassCastException.

    Оригинал статьи

3 комментария

Sant9Iga
спасибо большое
theGrass
Всегда пожалуйста :)
boyarskiy
К вопросу 6:
Integer[] nums = {1, 2, 3, 4};
List numList = Arrays.asList(nums);

размер списка numList после такого конвертирования будет неизменным, поскольку как указано у Б.Эккеля: «нижележащим представлением будет массив, не допускающий изменения размеров. Вызов add() или delete() для такого списка приведет к попытке изменения размера массива, а это приведет к ошибке во время выполнения». Иными словами наш список является всего лишь обёрткой исходного массива. Из JavaDoc об Arrays.asList(): Returns a fixed-size list backed by the specified array.
А вот такая операция:
Collection<Integer> collection =
      new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));

уже возвращает полноценную коллекцию, которую можно изменять в размерах, так как мы уже работаем с чистым списком.
Как советовал Эккель, эффективнее использовать методы из Collections.addAll() — этот метод работает значительно быстрее:

Collections.addAll(collection, 11, 12, 13, 14, 15);
Integer[] moreInts = { 6, 7, 8, 9, 10 };
Collections.addAll(collection, moreInts);
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.