Anleitung zu EnumSet

1. Einleitung

In diesem Tutorial werden wir die EnumSet- Sammlung aus dem Paket java.util untersuchen und ihre Besonderheiten diskutieren.

Wir werden zuerst die Hauptmerkmale der Sammlung zeigen und danach die Interna der Klasse durchgehen, um ihre Vorteile zu verstehen.

Abschließend werden die wichtigsten Operationen behandelt und einige grundlegende Beispiele implementiert.

2. Was ist ein EnumSet?

Ein EnumSet ist eine spezialisierte Set- Sammlung für die Arbeit mit Enum- Klassen . Es implementiert die Set- Schnittstelle und erweitert sich von AbstractSet :

Obwohl AbstractSet und Abstract bieten Implementierungen für fast alle Methoden der Set und Sammlung Schnittstellen EnumSet überschreibt die meisten von ihnen.

Wenn wir ein EnumSet verwenden möchten, müssen wir einige wichtige Punkte berücksichtigen:

  • Es kann nur enthalten Enum - Werte und alle Werte müssen derselben gehören Enum
  • Es ist nicht möglich, Nullwerte hinzuzufügen , und es wird eine NullPointerException ausgelöst , um dies zu versuchen
  • Es ist nicht threadsicher , daher müssen wir es bei Bedarf extern synchronisieren
  • Die Elemente werden in der Reihenfolge gespeichert, in der sie in der Aufzählung deklariert sind
  • Es wird ein ausfallsicherer Iterator verwendet , der mit einer Kopie arbeitet, sodass keine ConcurrentModificationException ausgelöst wird, wenn die Auflistung beim Durchlaufen geändert wird

3. Warum EnumSet verwenden ?

Als Faustregel sollte EnumSet beim Speichern von Enum- Werten immer einer anderen Set- Implementierung vorgezogen werden.

In den nächsten Abschnitten werden wir sehen, was diese Sammlung besser macht als andere. Zu diesem Zweck zeigen wir kurz die Interna der Klasse, um ein besseres Verständnis zu erhalten.

3.1. Implementierungsdetails

EnumSet ist eine öffentliche abstrakte Klasse, die mehrere statische Factory-Methoden enthält, mit denen wir Instanzen erstellen können. Das JDK bietet zwei verschiedene Implementierungen - sind paketprivat und werden von einem Bitvektor unterstützt:

  • RegularEnumSet und
  • JumboEnumSet

RegularEnumSet verwendet eine einzelne Länge , um den Bitvektor darzustellen. Jedes Bit des langen Elements repräsentiert einen Wert der Aufzählung . Der i-te Wert der Aufzählung wird im i-ten Bit gespeichert, sodass Sie leicht erkennen können, ob ein Wert vorhanden ist oder nicht. Da long ein 64-Bit-Datentyp ist, kann diese Implementierung bis zu 64 Elemente speichern.

Andererseits verwendet JumboEnumSet ein Array langer Elemente als Bitvektor. Dadurch kann diese Implementierung mehr als 64 Elemente speichern. Es funktioniert ziemlich ähnlich wie das RegularEnumSet, führt jedoch einige zusätzliche Berechnungen durch, um den Array-Index zu finden, in dem der Wert gespeichert ist.

Es überrascht nicht, dass das erste lange Element des Arrays die 64 ersten Werte der Aufzählung speichert , das zweite Element die nächsten 64 und so weiter.

EnumSet- Factory-Methoden erstellen abhängig von der Anzahl der Elemente der Aufzählung Instanzen der einen oder anderen Implementierung :

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

Beachten Sie, dass nur die Größe der Enum- Klasse berücksichtigt wird , nicht die Anzahl der Elemente, die in der Auflistung gespeichert werden.

3.2. Vorteile der Verwendung eines EnumSet

Durch die Implementierung eines EnumSet , dass wir oben beschrieben haben, alle Methoden in einer EnumSet werden arithmetische bitweise Operationen realisiert. Diese Berechnungen sind sehr schnell und daher werden alle Grundoperationen in einer konstanten Zeit ausgeführt.

Wenn wir EnumSet mit anderen Set- Implementierungen wie HashSet vergleichen , ist die erste normalerweise schneller, da die Werte in einer vorhersagbaren Reihenfolge gespeichert werden und nur ein Bit für jede Berechnung untersucht werden muss. Im Gegensatz zu HashSet muss der Hashcode nicht berechnet werden , um den richtigen Bucket zu finden.

Darüber hinaus ist ein EnumSet aufgrund der Art der Bitvektoren sehr kompakt und effizient. Daher benötigt es weniger Speicher mit allen damit verbundenen Vorteilen.

4. Hauptoperationen

Die meisten Methoden eines EnumSet funktionieren wie alle anderen Sets , mit Ausnahme der Methoden zum Erstellen von Instanzen.

In den nächsten Abschnitten werden alle Erstellungsmethoden detailliert dargestellt und der Rest der Methoden kurz behandelt.

In unseren Beispielen werden wir mit einer arbeiten Farbe Enum :

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

4.1. Schöpfungsmethoden

Die einfachsten Methoden zum Erstellen eines EnumSet sind allOf () und noneOf () . So können wir eine leicht erstellen können EnumSet alle Elemente unserer enthalten Farbe Enum:

EnumSet.allOf(Color.class);

Ebenso können wir noneOf () verwenden , um das Gegenteil zu tun und eine leere Sammlung von Farben zu erstellen :

EnumSet.noneOf(Color.class);

Wenn wir ein EnumSet mit einer Teilmenge der Enum- Elemente erstellen möchten, können wir die überladenen Methoden von () verwenden . Es ist wichtig, zwischen den Methoden mit einer festen Anzahl von Parametern bis zu 5 verschiedenen und denen zu unterscheiden, die varargs verwenden :

Der Javadoc gibt an, dass die Leistung der varargs- Version aufgrund der Erstellung des Arrays langsamer sein kann als die der anderen. Daher sollten wir es nur verwenden, wenn wir anfänglich mehr als 5 Elemente hinzufügen müssen.

Eine andere Möglichkeit, eine Teilmenge einer Aufzählung zu erstellen, ist die Verwendung der range () -Methode:

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

Im obigen Beispiel enthält das EnumSet alle Elemente von Gelb bis Blau. Sie folgen der in der Aufzählung definierten Reihenfolge :

[YELLOW, GREEN, BLUE]

Beachten Sie, dass es sowohl das erste als auch das letzte angegebene Element enthält.

Eine weitere nützliche Factory-Methode ist ComplementOf () , mit der wir die als Parameter übergebenen Elemente ausschließen können . Lassen Sie uns schaffen eine EnumSet mit allen Farbelemente außer Schwarz und Weiß:

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

Wenn wir diese Sammlung drucken, können wir sehen, dass sie alle anderen Elemente enthält:

[RED, YELLOW, GREEN, BLUE]

Schließlich können wir ein EnumSet erstellen, indem wir alle Elemente aus einem anderen EnumSet kopieren :

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

Intern wird die Klonmethode aufgerufen.

Darüber hinaus können wir auch alle Elemente aus jeder Sammlung kopieren , die Enum- Elemente enthält . Verwenden wir es, um alle Elemente einer Liste zu kopieren:

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

In diesem Fall enthält die listCopy nur die rote Farbe.

4.2. Sonstige Operationen

Der Rest der Vorgänge funktioniert genauso wie jede andere Set- Implementierung, und es gibt keinen Unterschied in der Verwendung.

Daher können wir leicht ein leeres EnumSet erstellen und einige Elemente hinzufügen:

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

Überprüfen Sie, ob die Sammlung ein bestimmtes Element enthält:

set.contains(Color.RED);

Iterieren Sie über die Elemente:

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

Oder entfernen Sie einfach Elemente:

set.remove(Color.RED);

Dies gilt natürlich auch für alle anderen Operationen, die ein Set unterstützt.

5. Schlussfolgerung

In diesem Artikel haben wir die Hauptfunktionen von EnumSet , seine interne Implementierung und wie wir davon profitieren können, gezeigt.

Wir haben auch die wichtigsten Methoden behandelt und einige Beispiele implementiert, um zu zeigen, wie wir sie verwenden können.

Wie immer ist der vollständige Quellcode der Beispiele auf GitHub verfügbar.