Это коллекция api интерфейсов предназначенных для разработки программ на java

Обновлено: 06.07.2024

Интерфейс — это ссылочный тип в Java. Он схож с классом. Это совокупность абстрактных методов. Класс реализует интерфейс, таким образом наследуя абстрактные методы интерфейса.

Вместе с абстрактными методами интерфейс в Java может содержать константы, обычные методы, статические методы и вложенные типы. Тела методов существуют только для обычных методов и статических методов.

Далее разберём зачем нужны интерфейсы в Java и для чего используются, разницу абстрактного класса и интерфейса.

Содержание

Написание интерфейса схоже с написанием класса. Но класс описывает атрибуты и поведения объекта. И интерфейс содержит поведения, которые класс реализует.

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

Чем похожи класс и интерфейс?

Интерфейс схож с классом следующим образом:

  • Интерфейс может содержать любое количество методов.
  • Интерфейс записан в файле с расширением .java, и имя интерфейса совпадает с именем файла.
  • Байт-код интерфейса находится в .class файле.
  • Интерфейсы появляются в пакетах, и их соответствующий файл байт-кода должен быть в структуре каталогов, которая совпадает с именем пакета.

Чем отличается класс от интерфейса?

Однако, интерфейс всё же отличается от класса. Отличие интерфейса от класса в Java:

  • Вы не можете создать экземпляр интерфейса.
  • В интерфейсе не содержатся конструкторы.
  • Все методы в интерфейсе абстрактные.
  • Интерфейс не может содержать поля экземпляров. Поля, которые могут появиться в интерфейсе, обязаны быть объявлены и статическими, и final.
  • Интерфейс не расширяется классом, он реализуется классом.
  • Интерфейс может расширить множество интерфейсов.

Объявление интерфейсов

Ключевое слово interface используется для объявления интерфейса. Вот пример того, как можно создать интерфейс:

Пример 1

Интерфейсы имеют следующие свойства:

  • Интерфейс абстрактный косвенно. Вам не нужно использовать ключевое слово abstract во время объявления интерфейса.
  • Каждый метод в интерфейсе косвенно абстрактным, поэтому ключевое слово abstract не нужно.
  • Методы в интерфейсе косвенно публичны.

Пример 2

Реализация интерфейса

Когда класс реализует интерфейс, вы можете представить себе, что класс словно подписывает контракт с интерфейсом, соглашаясь совершить конкретные его поведения. Если класс не исполняет все поведения интерфейса, то класс должен объявить себя абстрактным.

Класс использует ключевое слово implements для реализации интерфейса. Ключевое слово implements появляется при объявлении класса в его расширенной части.

Пример

При переопределении методов в интерфейсе, нужно следовать некоторым правилам:

  • Проверенные исключения не должны быть объявлены по методам реализации, отличным от тех, которые были объявлены методом интерфейса или подклассами тех, которые были объявлены методом интерфейса.
  • Подпись метода интерфейса и того же типа или подтипа возврата должна поддерживаться (сохраняться) при переопределении методов.
  • Сам класс реализации может быть абстрактным, а если это так, то методы интерфейса не должны быть реализованы.

При реализации интерфейсов есть некоторые правила:

  • Класс может реализовать более одного интерфейса за раз.
  • Класс может расширить только один класс, но реализовать множество интерфейсов.
  • Интерфейс может расширить другой интерфейс таким же образом, как класс расширяет другой класс.

Расширение интерфейсов

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

Приведённый интерфейс Sports расширен интерфейсами Hockey и Football.

Пример

Интерфейс Hockey имеет четыре метода, но он наследует два из Sports; таким образом, класс, который реализует Hockey, должен реализовать все шесть методов. Подобно этому, класс, который реализует Football, должен определить три метода из Football и два метода из Sports.

Расширение множества интерфейсов

Класс в Java может расширить только один родительский класс. Множественное наследование невозможно. Однако интерфейсы не классы, и интерфейс может расширить более чем один родительский интерфейс.

Ключевое слово extends используется лишь раз, а родительские интерфейсы объявляются через запятую.

Например, если интерфейс Hockey расширил и Sports, и Event, то объявление выглядело бы так:

Интерфейсы тегов

Самое распространённое использование расширения интерфейсов происходит тогда, когда родительский интерфейс не содержит каких-либо методов. Например, интерфейс MouseListener в пакете java.awt.event расширил java.util.EventListener, который определяется так:

Интерфейс без методов в нём называется интерфейсом тегов. Есть две простые дизайнерские цели для интерфейсов тегов:

Создаёт общего родителя – как в случае с интерфейсом EventListener, который расширяется множеством других в Java API, вы можете использовать интерфейс тегов, чтобы создать общего родителя среди группы интерфейсов. Например, когда интерфейс расширяет EventListener, то JVM знает, что этот конкретный интерфейс будет использоваться в сценарии делегирования событий.

Добавляет тип данных в класс – эта ситуация является источником термина «тегирование». Класс, который реализует интерфейс тегов, не должен определять какие-либо методы (т.к. интерфейс не имеет таковых), но класс становится типом интерфейса через полиморфизм.

Java

Программирование и разработка

Java

Интерфейс разработки приложений (API) — это вычислительный интерфейс, который определяет взаимодействие между программным обеспечением. API-интерфейсы позволяют разработчикам не углубляться в исходный код некоторых сторонних программ. Они упрощают программирование, абстрагируя базовую реализацию и открывая только те объекты или действия, которые необходимы разработчику. Таким образом, разработчикам не нужно разбираться в операциях файловой системы, происходящих за кулисами.

При разработке на Java большинство основных задач программирования выполняются классами и пакетами API, что помогает минимизировать количество строк, написанных в одном фрагменте кода.

Java Development Kit (JDK) состоит из трех основных компонентов, а именно:

  • Компилятор Java;
  • Виртуальная машина Java (JVM);
  • Интерфейс программирования приложений Java (API).

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

Типы API

Типы API

Существует множество видов API. Существует около 15 000 общедоступных API-интерфейсов и многие тысячи частных API-интерфейсов, которые компании используют для расширения своих внутренних и внешних возможностей.

Зачем нам нужен API в Java?

Автоматизация: с помощью Java API работа может контролироваться компьютерными системами, участие программистов не требуется. С помощью API компании-разработчики программного обеспечения могут обновлять рабочие процессы, чтобы сделать их более быстрыми и эффективными.

Применение: поскольку API-интерфейсы Java могут легко получить доступ к программным компонентам, существует гораздо большая гибкость в доставке данных и услуг.

Эффективность: после предоставления доступа к Java API созданный контент может быть немедленно выпущен и доступен для каждого канала. Это позволяет быстро распространять контент.

Интеграция: API Java упрощают встраивание контента. Это обеспечивает дополнительную динамическую доставку данных.

Зачем нам нужен API в Java

Действия, которые вы можете выполнять через API

API может выполнять четыре типа действий:

  • GET запрашивает данные с сервера;
  • POST отправляет изменения от клиента к серверу;
  • PUT изменяет или дополняет существующие данные;
  • УДАЛИТЬ: удаляет существующие данные.

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

Вот способы, которыми вы можете сделать запрос на сервере:

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

Еще один хороший способ получить данные из API — создать URL-адрес из существующей документации API. Запрос API обычно не сильно отличается от обычного URL-адреса браузера, но возвращаемые данные будут в форме, удобной для чтения компьютерами.

Заключение

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

VAVR (известная ранее, как Javaslang) — это некоммерческая функциональная библиотека для Java 8+. Она позволяет писать функциональный Scala-подобный код в Java и служит для уменьшения количества кода и повышения его качества. Сайт библиотеки.

Под катом — перевод статьи, систематизирующей информацию по API Коллекций Vavr.

Перевел @middle_java

1. Обзор

Библиотека Vavr, известная ранее, как Javaslang, является функциональной библиотекой для Java. В этой статье мы исследуем ее мощный API коллекций.

Дополнительные сведения об этой библиотеке см. в этой статье.

2. Персистентные Коллекции

Персистентная коллекция при изменении создает новую версию коллекции, не изменяя при этом текущую версию.

Поддержка нескольких версий одной коллекции может привести к неэффективному использованию ЦП и памяти. Однако, библиотека коллекций Vavr преодолевает это, расшаривая структуру данных между различными версиями коллекции.

Это принципиально отличается от unmodifiableCollection() из утилитного Java класса Collections , который просто предоставляет оболочку (wrapper) для базовой коллекции.

Попытка изменить такую коллекцию приводит к UnsupportedOperationException вместо создания новой версии. Более того, базовая коллекция по-прежнему изменяема через прямую ссылку на нее.

3. Traversable

Traversable — это базовый тип всех коллекций Vavr. Этот интерфейс определяет методы, общие для всех структур данных.

Он предоставляет некоторые полезные методы по-умолчанию, такие как size() , get() , filter() , isEmpty() и другие, которые наследуются суб-интерфейсами.

Исследуем далее библиотеку коллекций.

4. Seq

Начнем с последовательностей.

Интерфейс Seq представляет собой последовательные структуры данных. Это родительский интерфейс для List , Stream , Queue , Array , Vector и CharSeq . Все эти структуры данных имеют свои уникальные свойства, которые мы рассмотрим ниже.

4.1. List

List — это энергично вычисляемая (eagerly-evaluated, операция выполняется, как только становятся известны значения ее операндов) последовательность элементов, расширяющих интерфейс LinearSeq .

Персистентные List конструируются рекурсивно с использованием головы и хвоста:

  • Голова — первый элемент
  • Хвост — список, содержащий остальные элементы (этот список также формируется из головы и хвоста)

Можно также использовать статический метод empty() для создания пустого List и метод ofAll() для создания List из типа Iterable :


Рассмотрим некоторые примеры манипулирования списками.

Мы можем использовать метод drop() и его варианты для удаления первых N элементов:


drop(int n) удаляет n элементов из списка, начиная с первого элемента, в то время как dropRight() делает то же самое, начиная с последнего элемента в списке.

dropUntil() удаляет элементы из списка до тех пор, пока предикат не станет равен true , в то время как dropWhile() удаляет элементы, пока предикат равен true .

Также есть методы dropRightWhile() и dropRightUntil() , которые удаляют элементы, начиная справа.

Далее, take(int n) используется для извлечения элементов из списка. Он берет n элементов из списка, а затем останавливается. Существует также takeRight(int n) , который берет элементы, начиная с конца списка:


Наконец, takeUntil() берет элементы из списка до тех пор, пока предикат не станет равен true . Существует вариант takeWhile() , который также принимает аргумент-предикат.

Кроме того, в API есть и другие полезные методы, например, даже distinct() , который возвращает список элементов с удаленными дубликатами, а также distinctBy() , который принимает Comparator для определения равенства.

Очень интересно, что также есть intersperse() , который вставляет элемент между каждым элементом списка. Это может быть очень удобно для операций со String :


Хотите разделить список на категории? И для этого есть API:


Метод group(int n) разделяет List на группы из n элементов каждая. Метод groupdBy() принимает Function , содержащую логику разделения списка и возвращает Map с двумя элементами: true и false .

Ключ true мапится на List элементов, удовлетворяющих условию, указанному в Function . Ключ false мапится на List элементов, не удовлетворяющих этому условию.

Как и ожидалось, при изменении List , исходный List на самом деле не изменяется. Вместо этого всегда возвращается новая версия List .

Мы также можем взаимодействовать с List , используя семантику стека — извлечение элементов по принципу «последним вошел, первым вышел» (LIFO). В этом смысле, для манипулирования стеком существуют такие методы API, как peek() , pop() и push() :


Функция pushAll() используется для вставки диапазона целых чисел в стек, а функция peek() — для извлечения головного элемента стека. Существует также метод peekOption() , который может обернуть (wrap) результат в объект Option .

В интерфейсе List есть и другие интересные и действительно полезные методы, которые тщательно задокументированы в Java docs.

4.2. Queue

Неизменяемая (immutable) Queue хранит элементы, позволяя извлекать их по принципу FIFO (первым вошел, первым вышел).

Queue внутри состоит из двух связанных списков: переднего List и заднего List . Передний List содержит элементы, удаляемые из очереди, а задний List — элементы, поставленные в очередь.

Это позволяет привести операции постановки в очередь и удаления из очереди к сложности O(1). Когда при удалении из очереди в переднем List заканчиваются элементы, задний List реверсируется и становится новым передним List .

Давайте создадим очередь:


Функция dequeue() удаляет головной элемент из Queue и возвращает Tuple2<T, Q> . Первым элементом кортежа является головной элемент, удаленный из очереди, вторым элементом кортежа являются оставшиеся элементы Queue .

Мы можем использовать combination(n) , чтобы получить все возможные N комбинаций элементов в Queue :


Снова видно, что исходная Queue не изменяется во время добавления/удаления элементов из очереди.

4.3. Stream

Stream — это реализация лениво-связанного списка, который значительно отличается от java.util.stream . В отличие от java.util.stream , Stream Vavr хранит данные и лениво вычисляет последующие элементы.
Допустим, у нас есть Stream целых чисел:


При печати результата s.toString() в консоли будет отображаться только Stream(2, ?). Это означает, что был вычислен только головной элемент Stream , в то время как хвостовые элементы нет.

Вызов s.get(3) и последующее отображение результата s.tail() возвращает Stream(1, 3, 4, ?). Напротив, если не вызвать первым s.get(3) — что заставит Stream вычислить последний элемент — результатом s.tail() будет только Stream(1, ?). Это означает, что был вычислен только первый элемент хвоста.

Такое поведение может улучшить производительность и позволяет использовать Stream для представления последовательностей, которые (теоретически) бесконечно длинны.
Stream в Vavr является неизменяемым и может быть Empty или Cons . Cons состоит из головного элемента и лениво вычисляемого хвоста Stream . В отличие от List , Stream хранит в памяти только головной элемент. Хвостовые элементы вычисляются по необходимости.

Давайте создадим Stream из 10 положительных целых чисел и вычислим сумму четных чисел:


В отличие от Stream API из Java 8, Stream в Vavr — это структура данных для хранения последовательности элементов.

Поэтому, у него есть такие методы, как get() , append() , insert() и другие для манипулирования его элементами. Также доступны drop() , distinct() и некоторые другие методы, рассмотренные ранее.

Наконец, давайте быстро продемонстрируем tabulate() в Stream . Этот метод возвращает Stream длиной n , содержащий элементы, являющиеся результатом применения функции:


Мы также можем использовать zip() для создания Stream из Tuple2<Integer, Integer> , который содержит элементы, образованные путем комбинирования двух Stream :

4.4. Array

Array — это неизменяемая индексированная последовательность, обеспечивающая эффективный произвольный доступ. Он основан на Java массиве объектов. По сути, это Traversable оболочка для массива объектов типа T .

Можно создать экземпляр Array с помощью статического метода of() . Кроме того, можно создать диапазон элементов с помощью статических методов range() и rangeBy() . Метод rangeBy() имеет третий параметр, который позволяет определить шаг.

Методы range() и rangeBy() будут создавать элементы, начиная только с начального значения до конечного значения минус один. Если нам нужно включить конечное значение, можно использовать rangeClosed() или rangeClosedBy() :


Давайте поработаем с элементами с использованием индекса:

4.5. Vector

Vector — это что-то среднее между Array и List , предоставляющее другую индексированную последовательность элементов, позволяющую осуществлять и произвольный доступ, и модификацию за константное время:

4.6. CharSeq

CharSeq — это объект-коллекция для представления последовательности примитивных символов. По сути — это оболочка для String с добавлением операций коллекции.

Чтобы создать CharSeq необходимо выполнить следующее.

5. Set

В этом разделе рассматриваются различные реализации Set в библиотеке коллекций. Уникальная особенность структуры данных Set заключается в том, что она не допускает повторяющихся значений.

Существуют различные реализации Set . Основная из них — HashSet . TreeSet не допускает повторяющихся элементов и может быть отсортирован. LinkedHashSet сохраняет порядок вставки элементов.

Давайте рассмотрим более подробно эти реализации одну за другой.

5.1. HashSet

HashSet имеет статические фабричные методы для создания новых экземпляров. Некоторые из которых мы изучили ранее в этой статье, например of() , ofAll() и вариации методов range() .

Разницу между двумя set можно получить с помощью метода diff() . Также, методы union() и intersect() возвращают объединение и пересечение двух set:


Мы также можем выполнять основные операции, такие как добавление и удаление элементов:


Реализация HashSet основана на Hash array mapped trie (HAMT), которая может похвастаться превосходной производительностью по сравнению с обычной HashTable и ее структура делает ее подходящей для поддержки персистентных коллекций.

5.2. TreeSet

Неизменяемый TreeSet является реализацией интерфейса SortedSet . Он хранит набор отсортированных элементов и реализуется с применением бинарных деревьев поиска. Все его операции выполняются за время O(log n).

По умолчанию элементы TreeSet сортируются в их натуральном порядке.
Давайте создадим SortedSet используя натуральный порядок сортировки:


Чтобы упорядочить элементы кастомным образом, передайте экземпляр Comparator при создании TreeSet . Можно также создать строку из набора элементов:

5.3. BitSet

В коллекциях Vavr также присутствует неизменяемая реализация BitSet . Интерфейс BitSet расширяет интерфейс SortedSet . BitSet можно создать с помощью статических методов в BitSet.Builder .
Как и в других реализациях структуры данных Set , BitSet не позволяет добавлять в набор повторяющиеся записи.

Он наследует методы для манипулирования из интерфейса Traversable . Обратите внимание, что он отличается от java.util.BitSet из стандартной библиотеки Java. Данные BitSet не могут содержать значения String .

Рассмотрим создание экземпляра BitSet с использованием фабричного метода of() :


Для выбора первых четырех элементов BitSet мы использовали команду takeUntil() . Операция вернула новый экземпляр. Обратите внимание, что метод takeUntil() определен в интерфейсе Traversable , который является родительским интерфейсом для BitSet .

Другие методы и операции, описанные выше, определенные в интерфейсе Traversable , также применимы к BitSet .

6. Map

Map — это структура данных «ключ-значение». Map в Vavr является неизменяемой и имеет реализации для HashMap , TreeMap и LinkedHashMap .

Как правило, контракты map не допускают дублирование ключей, в то время как повторяющиеся значения, сопоставленные с различными ключами, могут быть.

6.1. HashMap

HashMap — это реализация неизменяемого интерфейса Map . Он хранит пары «ключ-значение», используя хэш-код ключей.

Map в Vavr использует Tuple2 для представления пар «ключ-значение» вместо традиционного типа Entry :


Как и HashSet , реализация HashMap основана на Hash array mapped trie (HAMT), что приводит к константному времени почти для всех операций.
Элементы map можно фильтровать по ключам с помощью метода filterKeys() или по значениям с помощью метода filterValues() . Оба метода принимают Predicate в качестве аргумента:


Также можно преобразовать элементы map с помощью метода map() . Давайте, например, преобразуем map1 в Map<String, Integer> :

6.2. TreeMap

Неизменяемая TreeMap является реализацией интерфейса SortedMap . Как и в случае TreeSet , для кастомной сортировки элементов TreeMap используется экземпляр Comparator .
Продемонстрируем создание SortedMap :


По умолчанию, записи TreeMap сортируются в натуральном порядке ключей. Однако, можно указать Comparator , который будет использоваться для сортировки:


Как и в случае TreeSet , реализация TreeMap также создана с применением дерева, следовательно, его операции имеют время O(log n). Метод map.get(key) возвращает Option , который содержит значение указанного ключа map.

7. Совместимость с Java

API коллекций Vavr полностью совместим с фреймворком коллекций Java. Посмотрим, как это делается на практике.

7.1. Преобразование из Java в Vavr

Каждая реализация коллекции в Vavr имеет статический фабричный метод ofAll() , который принимает java.util.Iterable . Это позволяет создать коллекцию Vavr из коллекции Java. Аналогично, другой фабричный метод ofAll() принимает непосредственно Java Stream .

Чтобы преобразовать List Java в неизменяемый List :


Другой полезной функцией является collector() , который можно использовать совместно с Stream.collect() для получения коллекции Vavr:

7.2. Преобразование из Vavr в Java

Интерфейс Value имеет множество методов для преобразования из типа Vavr в тип Java. Эти методы имеют формат toJavaXXX() .

Рассмотрим пару примеров:


Также мы можем использовать Java 8 Collectors для сбора элементов из коллекций Vavr:

7.3. Представления Коллекций Java

Кроме того, библиотека предоставляет так называемые представления коллекций, которые работают лучше при преобразовании в коллекции Java. Методы преобразования, приведенные в предыдущем разделе, перебирают (итерируют) все элементы для создания коллекции Java.

Представления, с другой стороны, реализуют стандартные интерфейсы Java и делегируют вызовы методов базовой коллекции Vavr.

На момент написания этой статьи поддерживается только представление List . Каждая последовательная коллекция имеет два метода: один для создания неизменяемого представления, другой для изменяемого.

Вызов методов для изменения на неизменяемом представлении приводит к исключению UnsupportedOperationException .

Давайте рассмотрим пример:


Чтобы создать неизменяемое представление:

8. Выводы

В этом уроке мы узнали о различных функциональных структурах данных, предоставляемых API Коллекций Vavr. Есть еще полезные и производительные методы API, которые можно найти в Java doc и руководстве пользователя коллекций Vavr.

Наконец, важно отметить, что библиотека также определяет Try , Option , Either и Future , которые расширяют интерфейс Value и, как следствие, реализуют интерфейс Java Iterable . Это означает, что в некоторых ситуациях они могут вести себя, как коллекции.

Полный исходный код для всех примеров в этой статье можно найти на Github.

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

Что такое Collections Framework

Это набор различных интерфейсов для работы с группами объектов при программировании на Java . Коллекции позволяют производить манипуляции с массивами (Array), очередями (Queue, Stack), списками (List), ключами и значениями (Map) и другими типами объектов.

Любая коллекция на Java строго структурирована: одни интерфейсы подчиняют другие, последние же — расширяют функционал своих «‎‎старших братьев» по иерархии.

Структура Java Collections Framework

В основе иерархической структуры Java Collections Framework лежит интерфейс Collection. Это главенствующий интерфейс. Общая коллекция вмещает в себя все остальные виды коллекций и позволяет работать с группами объектов благодаря основным методам, среди которых:

  • add(…) — добавить;
  • remove(…) — убрать;
  • clear(…) — очистить.

Интерфейс Collection наследуется от Iterable, а значит все подчиненные ему коллекции являются итерируемыми вне зависимости от типа их реализации. Благодаря этому в Collection вы можете получить итератор, который позволит вам пройтись по другим интерфейсам коллекций.

Итератор позволит пройтись по другим интерфейсам коллекций

Итератор позволит пройтись по другим интерфейсам коллекций

Интерфейсу Collections подчинены три главных интерфейса:

  • Set — позволяет работать с множествами данных;
  • List — взаимодействует со списками данных;
  • Queue — работает с очередью.

Интерфейс Map работает с данными типа «‎‎ключ/значение». Он стоит в этом списке отдельно, и чуть позже я объясню, почему это так. А пока давайте пройдемся по основным типам коллекций и выясним, за что отвечает каждая из них.

Интерфейс List и его реализации — ArrayList и LinkedList

List — это упорядоченная коллекция, наиболее часто используемая в Java Collections Framework. Этот интерфейс контролирует, где вставлен каждый элемент списка. При работе с List пользователю доступны элементы списка по их целочисленному индексу (позиции в списке).

Работа с интерфейсом List

Работа с интерфейсом List

Интерфейс List имеет две стандартные реализации — ArrayList и LinkedList.

Смысл в том, что можно написать и другие реализации, но в JDK уже есть две, которые доступны «‎‎из коробки».

ArrayList содержит внутри себя массив, длина которого будет увеличиваться автоматически при добавлении в него новых элементов.

Вторая имплементация интерфейса List — класс LinkedList. Это список с двумя связями, где каждый элемент содержит ссылку на предшествующий и следующий элементы списка.

Вторая имплементация интерфейса List — класс LinkedList

Вторая имплементация интерфейса List — класс LinkedList

ArrayList и LinkedList — идентичны, то есть их методы полностью совпадают. Они оба могут добавлять и извлекать элементы из любой части списка. Так в чем же отличие? Главная разница между ними в цикломатической сложности операций — количестве линейно независимых маршрутов, проходящих через код программы. Чем меньшее количество маршрутных точек будет проходить каждая отдельно взятая команда, тем быстрее будет работать программа.

Для наглядности различий в цикломатической сложности между двумя классами рассмотрим таблицу:

Различия в цикломатической сложности между двумя классами

Различия в цикломатической сложности между классами LinkedList и ArrayList

Как видите, основные преимущества класса LinkedList связаны с операциями (iterator.remove() и ListIterator.add(E element) . Их цикломатическая сложность всегда будет равна О(1) . В случае добавления add(E element) лучше использовать LinkedList, а вот для get(int index) — ArrayList.

Так же дела обстоят с оператором add(int index, E element) . Цикломатическая сложность этой операции для LinkedList будет ниже только в том случае, если сам индекс будет равен нулю. Для всех остальных случаев разумнее использовать именно ArrayList.

Это же касается и операции remove : для класса LinkedList ее цикломатическая сложность будет вдвое меньше, чем для ArrayList. Забегая наперед скажу, что именно ArrayList используют в подавляющем случае работ в интерфейсе List.

Интерфейс Set

Set — это коллекция, которая не содержит повторяющихся элементов. Этот интерфейс создан для моделирования абстракций математического множества.

Интерфейс Set

Иерархия Set включает в себя два интерфейса — SortedSet и NavigableSet — и три основных класса. О последних расскажу подробнее:

  • HashSet — коллекция, которая не позволяет хранение одинаковых элементов благодаря содержанию в себе объекта HashMap. Он использует для хранения данных хэш-таблицы.
  • LinkedHashSet — подобная HashSet, в которой элементы объединены между собой в порядковый список. В этом случае элементы хранятся в том же порядке, в котором и добавляются (благодаря содержанию в себе объекта LinkedHashMap).
  • TreeSet — коллекция, которая использует для хранения элементов упорядоченное по значениям дерево. TreeSet содержит в себе TreeMap — коллекцию, которая использует для хранения своих элементов сбалансированное красно-черное бинарное дерево. Благодаря этому операции add , remove и contains в этой коллекции займут гарантированное время, равное log(n) . А это — весомое преимущество по сравнению с другими имплементациями интерфейса Set .

Интерфейс Queue

Все мы не любим очереди: кому нравится напрасно тратить свое время в ожидании чего-то? Но вы точно полюбите очереди, если научитесь с ним работать 🙂 В Java Collections Framework за работу с очередью отвечает интерфейс Queue, который имеет довольно простую иерархию:

Интерфейс Queue

Queue способен упорядочить элементы в очереди по двум основным типам:

  • при помощи полученного в конструкторе интерфейса Comparator, который сравнивает новые объекты в очереди с предыдущими;
  • с помощью интерфейса Comparable, который необходим для упорядочения элементов в соответствии с их натуральным порядком.

Интерфейс Map

Это коллекция предназначена для работы с данными типа «‎‎ключ/значение». Под ключом мы имеем ввиду объект, который используем для извлечения данных, которые он в себе содержит. В структурном плане Map не наследует коллекцию Iterable и имеет ряд уникальных методов, характерных только для него.

Интерфейс Map

У этого интерфейса схожая с Set структурная иерархия классов. Map реализуется с помощью четырех имплементаций: HashMap, TreeMap (о них я писал выше), LinkedHashMap и WeakHashMap:

  • LinkedHashMap расширяет функционал HashMap и способен организовать перебор элементов в первоначальном порядке. То есть при осуществлении итерации по методу LinkedHashMap все объекты будут возвращены в строгом порядке их добавления.
  • WeakHashMap — класс, который организовывает слабые ссылки объектов со своими ключами. Используется для динамических объектов. Например, при построении систем с функцией сбора мусора. Она не вносится в перечень объектов, подлежащих удалению из-за того, что игнорируется сборщиком мусора при выявлении объектов, которые в ней содержаться.

Коллекции в Java достаточно объемны. Они содержат в себе массу интерфейсов и реализаций, о которых точно можно написать не одну статью.

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

Правильное понимание принципов работы с коллекциями останется одним из главных навыков для качественного программирования на Java.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Этот материал – не редакционный, это – личное мнение его автора. Редакция может не разделять это мнение.

Highload нужны авторы технических текстов. Вы наш человек, если разбираетесь в разработке, знаете языки программирования и умеете просто писать о сложном!
Откликнуться на вакансию можно здесь .

Java — современный язык программирования высокого уровня, разработанный и выпущенный в 1995 году компанией Sun Microsystems. Изначально создавался для программирования бытовых электронных устройств. Однако с момента появления фактически используется для разработки клиентских приложений и серверного программного обеспечения.

По состоянию на август 2020 года Java занимает второе место в рейтинге самых популярных языков программирования. На сайтах для поиска работы опубликовано множество вакансий по запросу «Java-разработчик». В списке требований к соискателю, как правило, присутствует пункт «знание Java Core» или даже «уверенное знание Java Core».

Core Java — термин Sun, используемый для обозначения Java Standard Edition (Java SE), стандартной версии и набора связанных технологий, таких как Java VM, CORBA и так далее. Дополнительную ясность вносит книга с одноимённым названием на английском языке. В русском переводе она называется «Java. Библиотека профессионала. Том 1. Основы», автор — Хорстманн Кей С. Мы также будем ссылаться на «Java 8. Полное руководство» Герберта Шилдта.

  • базовые конструкции;
  • объектно-ориентированное программирование;
  • введение в Swing;
  • обобщённое программирование;
  • коллекции (JCF);
  • исключения (Exception);
  • потоки ввода-вывода;
  • многопоточное программирование.

Базовые конструкции

Если у вас есть опыт программирования на С/C++, то знакомство с Java пройдёт легко, поскольку базовый синтаксис этих языков очень похож.

Изучение начинается с написания простейшей программы, вроде Hello World, выводящей в консоль строку приветствия. Далее в разной степени изучается следующий «джентльменский набор»:

  • понятия класса и метода;
  • переменные и константы, их типы и основы работы с ними;
  • массивы;
  • работа со строками как объектами, String API;
  • арифметические и логические операции и выражения;
  • команды для написания нелинейных программ, включающие в себя условный оператор if-else, тернарный оператор, оператор множественного выбора switch, циклы с определённым числом повторений for, включая его вариант for each, c неопределённым числом повторений while и do-while;
  • операторы для изменения порядка выполнения break и continue;
  • введение в пакетную систему Java, импорт классов, создание объектов, вызов статических и нестатических методов.

В этот раздел также входят навыки по установке и настройке JDK, вызову компилятора и виртуальной машины JVM в командной строке.

Материал по базовым конструкциям можно найти в упомянутых выше книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — глава 3;
  • «Java 8. Полное руководство» — главы 3, 4, 5.

Объектно-ориентированное программирование

Объектно-ориентированное программирование (ООП) в настоящее время активно вытесняет «структурные» и «процедурные» подходы, разработанные в 1970-х годах, фактически становясь доминирующей методикой. Java — это объектно-ориентированный язык, и для его продуктивного использования необходимо владеть ООП. Для ООП требуется иной образ мышления по сравнению с подходом, типичным для процедурных языков.

Изучение ООП начинается с рассмотрения класса — элемента, составляющего основу Java. Поля класса содержат данные, методы — код. Методы могут иметь параметры и возвращать значения. Конструкторы — особые методы. Объекты создаются на основании классов. Инкапсуляция — первый принцип ООП — подразумевает связывание и сокрытие. Изучаются модификаторы полей и методов: public, protected, private. Вводится понятие перегрузки методов. Затем рассматриваются понятия и механизмы наследования и полиморфизма. Разъясняется разница между статическими полями и методами и нестатическими. Обсуждаются абстрактные классы и методы, вводится понятие интерфейса.

Материал по этому разделу можно найти в книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — главы 4, 5, 6;
  • «Java 8. Полное руководство» — главы 6, 7, 8.

Введение в Swing

Swing — это набор классов для создания графического интерфейса пользователя (GUI) в Java-программах. В сравнении с ранее использовавшейся библиотекой AWT, у библиотеки Swing есть ряд преимуществ:

  • богатый набор интерфейсных примитивов;
  • настраивающийся внешний вид на различных платформах (look and feel);
  • раздельная архитектура «модель-вид» (model-view);
  • встроенная поддержка HTML.

Изучение Swing начинается с написания пустого окна на основании класса JFrame. Затем изучается назначение JPanel, JLabel, JButton и других элементов библиотеки java.awt, размещение элементов с помощью BorderLayout и других компоновщиков, обработка кликов по кнопке и кликов мыши по элементу, а также рисование на JPanel.

Материал по разделу можно найти в книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — главы 10, 11, 12;
  • «Java 8. Полное руководство» — главы 31, 32, 33.

Обобщённое программирование

Обобщения — это параметризованные типы. Они позволяют объявлять классы, интерфейсы и методы, где тип данных указан в виде параметра. Используя обобщения, можно создать универсальный класс, который будет правильно обрабатывать данные разных типов. Классы, интерфейсы или методы, оперирующие параметризованными типами, называют обобщёнными.

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

Материал по этому разделу можно найти в книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — глава 8;
  • «Java 8. Полное руководство» — глава 14.

Коллекции (JCF)

Пакет java.util содержит, помимо прочего, библиотеку коллекций (Java Collection Framework или JCF), позволяющую работать с множествами, хеш-таблицами, разными видами списков и так далее.

Коллекция — это объект, способный хранить группу однотипных элементов. Она также содержит методы для операций с данными. Основные преимущества классов JCF перед теми, которые можно разработать самостоятельно, следующие:

  • ускоряется процесс разработки и улучшается качество кода;
  • обеспечивается поддержка повторного использования кода;
  • производится стандартизация интерфейса ваших классов;
  • реализуется поддержка многопоточного доступа.

Основу библиотеки составляют открытые интерфейсы, которые можно использовать для создания собственных коллекций. Каждый интерфейс объявляет набор методов, которые необходимо реализовать в своей программе:

  • Collection — группа элементов (охватывает Set и List);
  • Set — множество элементов (без дублирования);
  • SortedSet — то же самое, что Set, только элементы упорядочены;
  • List — список;
  • Map — словарь, где каждый элемент имеет уникальный ключ;
  • SortedMap — то же, что и Map, однако элементы упорядочены;
  • Queue — интерфейс для работы с очередью.

Разумеется, интерфейсы были бы «пустыми», если бы не существовало встроенных классов, реализующих необходимые функции:

  • ArrayList — список List как массив элементов;
  • LinkedList — список List, выполняющий функции связанного списка;
  • HashSet — множество Set как хеш-таблица;
  • TreeSet — множество SortedSet, используемое как дерево;
  • HashMap — индексированный словарь хешей;
  • TreeMap — коллекция SortedMap древовидной структуры.

Материал по этому разделу можно найти в книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — глава 9;
  • «Java 8. Полное руководство» — глава 18.

Исключения (Exception)

В мире программирования возникновение ошибок и непредвиденных ситуаций при выполнении программы называют исключением. Они могут возникать в результате неправильных действий пользователя, отсутствии необходимого ресурса на диске или потери соединения с сервером по сети. Причинами исключений при выполнении программы также могут быть ошибки программирования или неправильное использование API. Ваша программа должна чётко знать, как поступать в такой ситуации. Для этого в Java предусмотрен механизм исключений.

При возникновении ошибки в процессе выполнения программы JVM создаёт объект нужного типа из иерархии исключений Java — множества возможных исключительных ситуаций, унаследованных от общего «предка» — класса Throwable. Исключение можно также создать вручную с помощью оператора throw. При этом выполнение основного кода программы прерывается, а обработчик исключений JVM пытается найти способ обработать исключение.

Блоки кода, для которых предусмотрена обработка исключений в Java, создаются с помощью конструкций try<>catch, try<>catch<>finally, try<>finally<>.

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

После выполнения блока catch управление передаётся в необязательный блок finally. Если подходящий блок catch не найден, JVM останавливает выполнение программы и выводит стек вызовов методов (stack trace), выполнив перед этим код блока finally при его наличии.

Материал по этому разделу можно найти в книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — глава 7;
  • «Java 8. Полное руководство» — глава 10.

Потоки ввода-вывода

Основная функциональность работы с потоками ввода-вывода сосредоточена в классах из пакета java.io.

Ключевым здесь является понятие потока. В программировании этот термин описывает различные концепции. Применительно к работе с файлами и вводом-выводом речь идёт о потоке (stream), который используется для чтения или записи информации (файлов, сетевых сокетов, консоли и так далее).

Поток связан с реальным физическим устройством с помощью системы ввода-вывода Java. Может быть определён поток, который связан с файлом и через который происходит чтение или запись в файл. Или поток может быть связан с сетевым сокетом, через который принимают или отправляют данные по сети. Все эти задачи — чтение и запись файлов, обмен информацией по сети, ввод-вывод в консоли — решаются в Java с помощью потоков.

Объект, из которого можно читать данные, называется потоком ввода, а объект, в который можно записывать — потоком вывода. Например, если надо прочитать содержимое файла, то используем поток ввода, а если надо записать в файл — поток вывода.

Материал по этому разделу можно найти в книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — глава 2;
  • «Java 8. Полное руководство» — глава 13, 20, 21.

Многопоточное программирование

Большинство языков программирования поддерживают такую важную функцию, как многопоточность, и Java не исключение. При помощи многопоточности мы можем создать в приложении несколько потоков, которые будут выполнять различные задачи одновременно. Если у нас, допустим, графическое приложение, которое посылает запрос к серверу или считывает и обрабатывает огромный файл, то без многопоточности у нас бы блокировался графический интерфейс на время выполнения задачи. А благодаря многопоточности мы можем выделить отправку запроса или любую другую «долгоиграющую» задачу в отдельный поток. Поэтому большинство современных приложений немыслимы без многопоточности.

Создать новый поток можно двумя способами:

  • с помощью интерфейса Runnable, создав класс, реализующий этот интерфейс;
  • наследованием от класса Thread — это даёт больше гибкости при работе с потоками благодаря методам класса Thread.

Материал по этому разделу можно найти в книгах:

  • «Java. Библиотека профессионала. Том 1. Основы» — глава 14;
  • «Java 8. Полное руководство» — глава 11.

Если вас заинтересовал язык программирования Java, предлагаю ознакомиться с моими статьями:

Читайте также: