Рубрики
Без рубрики

Руководство по EnumSet

Быстро узнайте о наборе перечислений и о том, как начать использовать его в практических сценариях.

Автор оригинала: Marcos Lopez Gonzalez.

1. введение

В этом уроке мы рассмотрим коллекцию EnumSet из пакета java.util и обсудим ее особенности.

Сначала мы покажем основные функции коллекции, а затем рассмотрим внутренние компоненты класса, чтобы понять его преимущества.

Наконец, мы рассмотрим основные операции, которые он предоставляет, и реализуем некоторые основные примеры.

2. Что такое перечисление

EnumSet – это специализированная Set коллекция для работы с enum классами . Он реализует интерфейс Set и расширяется из Абстрактного набора :

Несмотря на то, что AbstractSet и Abstract Collection предоставляют реализации почти для всех методов интерфейсов Set и Collection , EnumSet переопределяет большинство из них.

Когда мы планируем использовать EnumSet , мы должны учитывать некоторые важные моменты:

  • Он может содержать только перечисление значения , и все значения должны принадлежать одному и тому же перечислению
  • Он не позволяет добавлять нулевые значения , вызывая Исключение NullPointerException при попытке сделать это
  • Это не потокобезопасно , поэтому нам нужно синхронизировать его внешне, если потребуется
  • Элементы хранятся в том порядке, в котором они объявлены в перечисление
  • Он использует отказоустойчивый итератор , который работает с копией, поэтому он не будет вызывать исключение ConcurrentModificationException , если коллекция будет изменена при итерации по ней

3. Зачем использовать EnumSet

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

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

3.1. Детали реализации

EnumSet – это public абстрактный класс, содержащий несколько статических заводских методов, которые позволяют нам создавать экземпляры. JDK предоставляет 2 различные реализации – являются закрытыми для пакетов и поддерживаются битовым вектором:

  • RegularEnumSet и
  • JumboEnumSet

RegularEnumSet использует один длинный для представления битового вектора. Каждый бит элемента long представляет значение перечисления . I-е значение перечисления будет сохранено в i-м бите, поэтому довольно легко узнать, присутствует ли значение или нет. Поскольку long является 64-разрядным типом данных, эта реализация может хранить до 64 элементов.

С другой стороны, JumboEnumSet использует массив элементов long в качестве битового вектора. Это позволяет этой реализации хранить более 64 элементов. Он работает почти так же, как RegularEnumSet , но делает некоторые дополнительные вычисления, чтобы найти индекс массива, в котором хранится значение.

Неудивительно, что первый длинный элемент массива будет хранить 64 первых значения enum , второй элемент-следующие 64 и так далее.

Методы EnumSet factory создают экземпляры той или иной реализации в зависимости от количества элементов перечисления :

if (universe.length <= 64)
    return new RegularEnumSet<>(elementType, universe);
else
    return new JumboEnumSet<>(elementType, universe);

Имейте в виду, что он учитывает только размер класса enum , а не количество элементов, которые будут храниться в коллекции.

3.2. Преимущества использования набора перечислений

Из-за реализации EnumSet , которую мы описали выше, все методы в EnumSet реализуются с использованием арифметических побитовых операций. Эти вычисления очень быстры, и поэтому все основные операции выполняются в течение постоянного времени.

Если мы сравним EnumSet с другими реализациями Set , такими как HashSet , первая обычно быстрее, потому что значения хранятся в предсказуемом порядке, и для каждого вычисления необходимо проверять только один бит. В отличие от HashSet , нет необходимости вычислять hashcode , чтобы найти нужное ведро.

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

4. Основные операции

Большинство методов EnumSet работают так же , как и любой другой Set , за исключением методов создания экземпляров.

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

В наших примерах мы будем работать с Цветом | перечислением :

public enum Color {
    RED, YELLOW, GREEN, BLUE, BLACK, WHITE
}

4.1. Методы создания

Наиболее простыми методами создания EnumSet являются all Of() и none Of() . Таким образом, мы можем легко создать EnumSet , содержащий все элементы нашего Color перечисления:

EnumSet.allOf(Color.class);

Аналогично, мы можем использовать none Of () , чтобы сделать обратное и создать пустую коллекцию Color :

EnumSet.noneOf(Color.class);

Если мы хотим создать EnumSet с подмножеством элементов enum , мы можем использовать перегруженные из() методов . Важно различать методы с фиксированным числом параметров до 5 различных и тот, который использует varargs :

Javadoc утверждает, что производительность версии varargs может быть медленнее, чем у других, из-за создания массива. Поэтому мы должны использовать его только в том случае, если нам изначально нужно добавить более 5 элементов.

Другой способ создать подмножество перечисление является с помощью диапазон() метод:

EnumSet.range(Color.YELLOW, Color.BLUE);

В приведенном выше примере EnumSet содержит все элементы от Yellow до Синий. Они следуют порядку, определенному в перечислении :

[YELLOW, GREEN, BLUE]

Обратите внимание, что он включает в себя как первый, так и последний указанные элементы.

Другим полезным заводским методом является дополнение () , которое позволяет нам исключать элементы, передаваемые в качестве параметров . Давайте создадим EnumSet со всеми элементами Color , кроме черного и белого:

EnumSet.complementOf(EnumSet.of(Color.BLACK, Color.WHITE));

Если мы распечатаем эту коллекцию, то увидим, что она содержит все остальные элементы:

[RED, YELLOW, GREEN, BLUE]

Наконец, мы можем создать EnumSet , скопировав все элементы из другого EnumSet :

EnumSet.copyOf(EnumSet.of(Color.BLACK, Color.WHITE));

Внутренне он вызывает метод clone .

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

List colorsList = new ArrayList<>();
colorsList.add(Color.RED);
EnumSet listCopy = EnumSet.copyOf(colorsList);

В этом случае копия списка содержит только красный цвет.

4.2. Прочие Операции

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

Поэтому мы можем легко создать пустой EnumSet и добавить некоторые элементы:

EnumSet set = EnumSet.noneOf(Color.class);
set.add(Color.RED);
set.add(Color.YELLOW)

Проверьте, содержит ли коллекция определенный элемент:

set.contains(Color.RED);

Повторите элементы:

set.forEach(System.out::println);

Или просто удалите элементы:

set.remove(Color.RED);

Это, конечно, среди всех других операций, которые поддерживает Set .

5. Заключение

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

Мы также рассмотрели основные методы, которые он предлагает, и реализовали несколько примеров, чтобы показать, как мы можем их использовать.

Как всегда, полный исходный код примеров доступен на GitHub .