Урок 23. Введение в JCF Java


Этот урок является частью моего бесплатного курса Java для начинающих, посвященного принципам чистого кода. На нем мы познакомимся с Java Collections Framework (JCF). Коллекция - это слово с несколькими перегруженными значениями, так что, чтобы прояснить ситуацию, мы сначала обсудим значение этого слова.

Слово коллекция может означать:

  • повседневное значение - компиляция или группа вещей,
  • собрание интерфейсов и классов, составляющих структуру коллекций Java,
  • некоторая структура данных, такая как упаковка или контейнер, которая может содержать группу объектов, таких как массив,
  • интерфейс util.Collection, один из двух основных интерфейсов JCF, или
  • util.Collections - это служебный класс, который может помочь изменять или работать с коллекциями Java.
Что такое Framework Java Collections? Прежде всего, это фактически библиотека, набор универсальных интерфейсов и классов. Этот набор инструментов содержит различные интерфейсы коллекций и классы, которые служат более мощной объектно-ориентированной альтернативой массивам. Связанные с коллекцией служебные интерфейсы и классы также обеспечивают большую простоту использования.


Рисунок 1: иерархия классов и интерфейсов

Сначала мы более подробно рассмотрим интерфейс и иерархию классов для коллекций. В отличие от массивов, в Java все коллекции могут динамически увеличиваться или уменьшаться в размерах. Как я уже говорил, коллекции содержат группы объектов. Класс map может хранить сильно связанные пары объектов вместе, каждая пара состоит из ключа и значения. Значение не имеет определенной позиции, но может быть получено с помощью ключа, с которым оно сопряжено. Не волнуйтесь, если не можете понять все прямо сейчас, так как мы рассмотрим более подробно позже.

На рис. 1 показана иерархия классов и интерфейсов, расширяющих или реализующих интерфейс коллекции - было бы полезно хотя бы ознакомиться с перечисленными там названиями. Интерфейс коллекции находится поверх ряда подинтерфейсов и реализующих классов. Коллекция может содержать группу объектов различными способами, которые обеспечивают набор, список и очередь.

Набор (Set) определяется как группа уникальных объектов. То, что считается уникальным, определяется методом equals типа объекта, который он содержит. Другими словами, множество не может содержать два одинаковых объекта. Список (List) определяется как последовательность объектов. В отличие от набора, список может содержать повторяющиеся записи. Он также сохраняет свои элементы в том порядке, в котором они были вставлены. Очередь (Queue) имеет две стороны. Записи добавляются в его хвостовую часть, в то время как записи удаляются из верхней или головной части. Это часто описывается как "первый вход, первый выход” (FIFO), что часто очень похоже на ожидание в очереди в реальной жизни, т. е. первый человек, стоящий в очереди, - это первый человек, который покидает очередь.

Интерфейс Set

HashSet, LinkedHasSet И TreeSet

HashSet, LinkedHashSet и TreeSet - это реализации Set, расположенные вокруг левого конца иерархии интерфейса коллекции на Рис. 1. HashSet-это реализация по умолчанию, используемая в большинстве случаев. LinkedHashSet похож на комбинацию HashSet и List в том смысле, что он не допускает повторяющихся записей, как с наборами, но пересекает свои элементы в том порядке, в котором они были вставлены, как это сделал бы список. TreeSet будет постоянно держать все свои элементы в некотором отсортированном порядке. Имейте в виду, однако, что нет такой вещи, как бесплатный обед, и что каждая добавленная функция имеет определенную стоимость в плане ресурсов Java-машины.

SortedSet и NavigableSet

После рассмотрения трех классов, реализующих набор, давайте также рассмотрим два субинтерфейса, о которых мы еще не говорили. Как следует из названия, SortedSet - это набор со свойством, что он всегда сортируется. Интерфейс NavigableSet, добавленный с Java 6, позволяет нам перемещаться по отсортированному списку, предоставляя методы для извлечения следующего элемента, большего или меньшего, чем данный элемент набора.

Интерфейс списка

ArrayList и LinkedList

ArrayList является реализация по умолчанию для списка, расположенном в середине иерархии семейства на рисунке 1. Как и любая реализация списка, она допускает дублирование элементов и итерацию в порядке вставки. Поскольку он основан на массивах, он очень быстро повторяется и считывается, но очень медленно добавляет или удаляет элемент в случайных позициях, поскольку ему приходится перестраивать базовую структуру массива. Напротив, LinkedList позволяет легко добавлять или удалять элементы в любой позиции списка, в то время как чтение из случайных позиций происходит медленнее.

Vector

В качестве примечания мы кратко рассмотрим Vector, класс, который существует со времен JDK 1, даже до фреймворка Collections, который был добавлен с Java 2. короче говоря, его производительность неоптимальна, поэтому; лучше заменить его ArrayList или LinkedList.

Интерфейс очереди

Наконец, мы рассмотрим классы, реализующие очередь. Еще одна вещь, которую следует упомянуть о LinkedList, заключается в том, что, хотя он реализует список, на самом деле он также реализует очередь. Он делает это на основе того факта, что его фактическая реализация в виде двусвязного списка позволяет довольно легко реализовать и интерфейс очереди.

PriorityQueue

Помимо LinkedList, еще одной распространенной реализацией очереди является PriorityQueue. Это реализация, которая автоматически упорядочивает свои элементы. Он имеет функциональность, аналогичную TreeSet, за исключением того, что он позволяет дублировать записи.

Интерфейс Map


Рисунок 2: Интерфейс Map

Теперь мы рассмотрим интерфейс Map, который, как ни странно, не имеет никакого отношения к интерфейсу коллекции. Коллекция оперирует одним объектом, в то время как карта оперирует двумя: уникальным ключом (например, идентификационным номером транспортного средства и объектом, связанным с ключом, например автомобилем). Для извлечения объекта bp Map обычно используется его ключ. Map является корнем целого ряда интерфейсов и классов.

Hashtable, Hashmap и LinkedHashMap

Класс Hashtable был первой коллекцией в Java 1, основанной на структуре данных хэш-таблицы. К сожалению, как и Vector, этот класс устарел из-за своей неоптимальной производительности. Мы можем забыть об этом и вместо этого использовать другие реализации карт. HashMap - это реализация по умолчанию, которую вы будете использовать в большинстве случаев.

К сожалению, это затрудняет различие между ними. Как и Vector, этот класс устарел из-за своей неоптимальной производительности, поэтому давайте удалим его и забудем о нем. Вместо этого используйте один из других классов, реализующих интерфейс map. HashMap - это реализация по умолчанию, которую вы должны использовать в большинстве случаев. Map обычно не дает никаких гарантий относительно того, как она внутренне хранит свои элементы. Исключением из этого правила является LinkedHashMap, который позволяет повторять карту в порядке вставки. И последнее, но не менее важное: TreeMap - это постоянно сортируемая Map.

SortedMap

Теперь давайте рассмотрим интерфейсы, которые расширяют интерфейс Map. Как следует из названия, интерфейс SortedMap расширяет интерфейс Map и определяет контракт постоянно сортируемой Map. NavigableMap снова расширяет интерфейс SortedMap и добавляет методы для навигации по карте. Это позволяет вам получить все записи, меньшие или большие, чем данная запись, например.

На самом деле между Map и иерархией множеств есть много общего. Причина в том, что реализации набора фактически внутренне поддерживаются реализацией Map. И последнее, но не менее важное: вы, возможно, заметили, что классы коллекций Java часто содержат в своем имени структуру данных, на которой они основаны.

Чтобы выбрать лучшую коллекцию для данной ситуации, вы должны сначала сравнить конкретные характеристики структур данных, таких как массив, LinkedList, Hashtable или дерево. Короче говоря, нет единого наилучшего варианта, у каждого есть свои преимущества и недостатки. Я обещаю поговорить об этой очень волнующей теме в одном из последующих эпизодов. Оставайтесь с нами. Этот обзор коллекции и классов карт показал вам только часть всей истории. На следующем уроке я познакомлю вас с параллельными контейнерами фреймворка Java Collections.

Общая картина

Возможно, вы заметили, что классы коллекций Java часто содержат структуры данных, основанные на их названии. Чтобы выбрать наилучшую коллекцию для данной ситуации, вы должны сравнить и сопоставить свойства структур данных, таких как LinkedList, Hashtable или TreeSet, с текущей проблемой. Короче говоря, единого наилучшего варианта не существует, поскольку каждый из них имеет свои преимущества и недостатки.

На самом деле существует гораздо больше оснований для рассмотрения этого вопроса, поскольку этот обзор показал лишь крошечную часть огромного объема коллекций и классов карт. Вообще в Java Collections Framework есть даже параллельные контейнеры, которые используются для параллельного программирования.

Дженерики

Тема дженериков, по крайней мере, так же широка, как и фреймворк Java Collections. В контексте этой статьи мы обсудим только голый минимум, необходимый для понимания структуры коллекций. После этого краткого обзора вполне нормально иметь много открытых вопросов. Все будет объяснено одно за другим.

List<String> myList = new ArrayList<String>(100);
Обратите внимание на использование угловых скобок. На левой стороне, мы определяем переменную списке "myList" с строковым параметром в угловых скобках. Мы говорим компилятору, что переменная myList должна ссылаться только на некоторый список, содержащий строки.

Затем мы создаем объект типа ArrayList и снова сообщаем компилятору, что список должен содержать только строки. Другими словами, именно это делает контейнеры безопасными. Также обратите внимание на использование типа List для переменной вместо ArrayList. Это делает наш код более гибким. Вы создадите объект только один раз, но часто будете использовать его во многих местах. Тем не менее, когда вы объявляете список вместо ArrayList, вы можете заменить ArrayList на LinkedList позже, и все, что вам нужно было изменить, - это одна строка кода.

Collection<String> myList = new ArrayList<String>(100);
В случае, если вам действительно не нужны методы, специфичные для List, вы также можете просто использовать Collection вместо этого. Это хорошая идея - всегда использовать в качестве переменной наименее конкретный, самый маленький интерфейс, насколько это возможно. Обратите внимание на использование 100 в качестве аргумента конструктора ArrayList. В данном случае речь идет об оптимизации производительности. Поскольку ArrayList и все коллекции на основе hashtable внутренне работают с массивами, когда такая коллекция увеличивается в размере, она создает большие массивы на лету и переносит все содержимое из старого массива в новый.

Хотя это занимает некоторое дополнительное время, современное оборудование работает так быстро, что обычно это не должно быть проблемой. С другой стороны, знать точный или даже просто приблизительный размер коллекции лучше, чем довольствоваться размерами коллекции по умолчанию. Знание того, на каких структурах данных основаны коллекции Java, помогает лучше понять производительность в подобных случаях. Обращать внимание на такие мелкие детали - это часто разница между юниором и мидлом.

Map<VIN, Car> myMap = new HashMap<>(100);
Смотрите, как объявляется Map и как строится HashMap выше. Map - это отношение одного идентифицирующего ключевого элемента к одному ценностному элементу, и оба они могут быть разных типов. В приведенном выше примере VIN-идентификационный номер транспортного средства используется в качестве ключа, в то время как объект автомобиля является значением. Параметры типа добавляются в виде списка, разделенного запятыми, в угловых скобках. Начиная с Java 7, Если вы объявляете переменную и создаете объект все в одной строке, вы можете оставить вторую пару угловых скобок пустыми, поскольку компилятор выводит тип объекта из общего типа ссылочной переменной.

Пустые угловые скобки называются алмазным оператором, благодаря своему названию тому, как пустые скобки образуют ромбовидную форму. Однако то, что обсуждалось до сих пор, - это просто использование универсальных классов, где мы фиксируем конкретные параметры типа, которые будут использоваться при создании экземпляров. Все это возможно только в том случае, если заранее определен какой-либо метод, интерфейс или класс для использования в общем виде.

Написание универсального кода

Пример ниже показывает, в общем определенный интерфейс. В первой строке интерфейс определяется как один, работающий на двух универсальных типах, которые должны быть указаны позже. Когда эти типы заблокированы, типы, используемые интерфейсными методами, задаются автоматически. Если вы видите однобуквенные типы в коде, это может означать, что он может быть использован в общем виде.

public interface MyInterface<E, T> {
    E read();
    void process(T o1, T o2);
 }
Другие служебные интерфейсы

  • util.Iterator
  • lang.Iterable
  • lang.Comparable
  • lang.Comparator
Выше перечислены некоторые дополнительные интерфейсы утилит из фреймворка Java Collections. Они реализуются классами фреймворка или JDK в целом. Кроме того, они также могут быть реализованы вашими собственными классами, используя возможности и совместимость с платформой коллекций. Строго говоря, java. lang.Iterable не является частью фреймворка, а точнее сидит на нем сверху. Это супер-интерфейс java. util.Collection, что означает, что каждый класс, реализующий Collection, также реализует java.lang.Iterable.

Java.Util.Iterator

  • boolean hasNext();
  • E next();
  • void remove();
Итератор - это объект, который действует как пульт дистанционного управления для перебора вещей, часто коллекций. hasNext() возвращает true, если коллекция содержит больше элементов, next() возвращает следующий элемент в итерации, а remove() удаляет последний элемент, возвращенный итератором из его базовой коллекции.

Java.Lang.Iterable

  • Iterator iterator()
Iterable предоставляет только один метод, который возвращает итератор. Каждая коллекция, реализующая этот интерфейс, может быть использована в цикле for-each, что значительно упрощает использование ваших домашних коллекций. Чтобы воспользоваться использованием цикла for-each для вашей коллекции, вам нужно всего лишь выполнить два простых шага: сначала написать итератор для вашей коллекции и реализовать все его методы hasNext, next и remove. Во-вторых, реализуйте итерационный интерфейс, добавив метод итератора, который возвращает экземпляр реализации итератора, написанной на первом шаге.

Java.Lang.Comparable

  • int compareTo(T o)
Реализация Comparable определяет естественный порядок сортировки для ваших сущностей. Интерфейс содержит только один метод, который вам нужно реализовать, compareTo, который сравнивает ваш Comparable с T o, аргументом, представляющим другую сущность того же типа. Возвращает отрицательное целое число, если объект меньше заданного аргумента o, 0, если объект равен o, и положительное целое число, если объект больше o.

Что значит для одной вещи быть меньше или больше, чем для другой, вам предстоит определить. Из чисел легко следует, что 1 меньше 5. Но как насчет цветов? Это полностью зависит от того, что вы считаете естественным порядком ваших сущностей. Когда вы помещаете сопоставимые объекты в набор деревьев или карту деревьев, например, он будет использовать ваш пользовательский метод compareTo для автоматической сортировки всех элементов в вашей коллекции. Как вы можете видеть, фреймворк Java Collections был значительно разработан с учетом расширения, предлагая вам множество возможностей для подключения ваших собственных классов.

Java.Lang.Comparator

  • int compare(T o1, T o2)
Этот интерфейс очень похож на Comparable. Он позволяет определить дополнительные порядки сортировки, например, обратный порядок. Логика сортировки непосредственно не реализована в вашем классе сущностей. Вместо этого он определяется во внешнем классе стратегии сортировки, который может быть дополнительно присоединен к коллекции или методу сортировки для определения альтернативных порядков сортировки для ваших коллекций сущностей. Применяются те же правила для интерфейсного контракта Comparable: возвращает отрицательное целое число, если первый аргумент o1 меньше второго аргумента o2, 0, если оба аргумента равны, и положительное целое число, если o1 больше o2.

Коллекции и массивы

И последнее, но не менее важное: мы рассмотрим два класса java.util.Collections и java.util.Arrays. Как и швейцарский армейский нож, оба предоставляют статические вспомогательные методы, которые значительно повышают общую полезность классов коллекций. Коллекции предлагают такие методы, как сортировка, перемешивание, реверс, поиск, min и max. Массивы на самом деле очень похожи на коллекции, за исключением того, что они работают с необработанными массивами, то есть позволяют нам, например, сортировать или искать по массивам.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, уроки по java, java, JCF, коллекции




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Урок 20. Продолжение страницы контактов, или отправка писем с вложением Laravel
Уроки по SQL
Урок 23. Введение в области применения (scope) Laravel