Автор оригинала: 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 .
Кроме того, мы также можем скопировать все элементы из любой Коллекции , содержащей перечисление элементов . Давайте используем его для копирования всех элементов списка:
ListcolorsList = new ArrayList<>(); colorsList.add(Color.RED); EnumSet listCopy = EnumSet.copyOf(colorsList);
В этом случае копия списка содержит только красный цвет.
4.2. Прочие Операции
Остальные операции работают точно так же, как и любая другая реализация Set , и нет никакой разницы в том, как их использовать.
Поэтому мы можем легко создать пустой EnumSet и добавить некоторые элементы:
EnumSetset = 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 .