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

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

1. Вступление

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

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

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

2. Что такоеEnumSet

An EnumSet is a specialized Set collection to work with enum classes. Он реализует интерфейсSet и расширяется отAbstractSet:

image

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

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

  • It can contain only enum values, и все значения должны принадлежать одному и тому жеenum

  • It doesn’t allow to add null values, бросаяNullPointerException в попытке сделать это

  • It’s not thread-safe, поэтому нам нужно синхронизировать его внешне, если требуется

  • Элементы хранятся в том порядке, в котором они объявлены вenum

  • It uses a fail-safe iterator, который работает с копией, поэтому он не выдастConcurrentModificationException, если коллекция изменена при повторении по ней

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

Как показывает опыт,EnumSet should always be preferred over any other Set implementation when we are storing enum values.

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

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

EnumSet - это классpublicabstract, который содержит несколько статических фабричных методов, которые позволяют нам создавать экземпляры. JDK предоставляет две разные реализации -package-private и поддерживает битовый вектор:

  • RegularEnumSet и

  • JumboEnumSet

RegularEnumSet uses a single long to represent the bit vector. Каждый бит элементаlong представляет значениеenum. I-е значение перечисления будет сохранено в i-м бите, поэтому довольно легко узнать, присутствует ли значение или нет. Since long is a 64-bit data type, this implementation can store up to 64 elements.

С другой стороны,JumboEnumSet uses an array of long elements as a bit vector.This lets this implementation store more than 64 elements. Он работает почти так же, какRegularEnumSet, но выполняет некоторые дополнительные вычисления, чтобы найти индекс массива, в котором хранится значение.

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

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

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

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

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

Из-за реализацииEnumSet, описанной выше,all the methods in an EnumSet are implemented using arithmetic bitwise operations. Эти вычисления очень быстрые, и поэтому все основные операции выполняются за постоянное время.

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

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

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

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

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

В наших примерах мы будем работать сColorenum:

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

4.1. Творческие Методы

The most simple methods to create an EnumSet are allOf() and noneOf(). Таким образом, мы можем легко создатьEnumSet, содержащий все элементы нашего перечисленияColor:

EnumSet.allOf(Color.class);

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

EnumSet.noneOf(Color.class);

If we want to create an EnumSet with a subset of the enum elements we can use the overloaded of() methods. Важно различать методы с фиксированным количеством параметров (до 5 различных) и методы, использующиеvarargs:

image

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

Другой способ создать подмножествоenum - использовать методrange():

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

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

[YELLOW, GREEN, BLUE]

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

Another useful factory method is the complementOf() that allows us to exclude the elements passed as parameters. Давайте создадимEnumSet со всеми элементамиColor, кроме черного и белого:

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

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

[RED, YELLOW, GREEN, BLUE]

Наконец,we can create an EnumSet by copying all the elements from another EnumSet:

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

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

Кроме того,we can also copy all the elements from any Collection that contains enum elements. Давайте воспользуемся им, чтобы скопировать все элементы списка:

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

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

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, его внутреннюю реализацию и преимущества от его использования.

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

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