1. 序章

このチュートリアルでは、 java.utilパッケージのEnumSetコレクションを調べて、その特性について説明します。

最初にコレクションの主な機能を示し、その後、その利点を理解するためにクラスの内部を確認します。

最後に、それが提供する主な操作をカバーし、いくつかの基本的な例を実装します。

2. EnumSetとは

EnumSetは、列挙型クラスを操作するための特殊なSetコレクションです。 Set インターフェースを実装し、AbstractSetから拡張します。

AbstractSetおよびAbstractCollectionは、SetおよびCollectionインターフェイスのほぼすべてのメソッドの実装を提供しますが、 EnumSet それらのほとんどをオーバーライドします。

EnumSet を使用する場合は、いくつかの重要な点を考慮する必要があります。

  • 列挙値のみを含めることができ、すべての値は同じ列挙に属している必要があります
  • null値を追加することはできません、そうしようとしてNullPointerExceptionをスローします
  • スレッドセーフではないので、必要に応じて外部で同期する必要があります
  • 要素は、enumで宣言された順序に従って格納されます。
  • コピーで機能するフェイルセーフイテレータを使用するため、コレクションを反復処理するときにコレクションが変更されても、ConcurrentModificationExceptionはスローされません。

3. EnumSetを使用する理由

経験則として、列挙値を格納するときは、EnumSetを他のSet実装よりも常に優先する必要があります。

次のセクションでは、このコレクションが他のコレクションよりも優れている理由を説明します。 そのために、クラスの内部を簡単に示して理解を深めます。

3.1. 実装の詳細

EnumSet は、 public abstract クラスであり、インスタンスの作成を可能にする複数の静的ファクトリメソッドが含まれています。 JDKは、 package-private であり、ビットベクトルに裏打ちされた2つの異なる実装を提供します。

  • RegularEnumSetおよび
  • JumboEnumSet

RegularEnumSetは、単一のlongを使用してビットベクトルを表します。 の各ビット長いです要素はの値を表します列挙型 。 列挙型のi番目の値はi番目のビットに格納されるため、値が存在するかどうかを簡単に知ることができます。  longは64ビットのデータ型であるため、この実装では最大64個の要素を格納できます。

一方、 JumpoEnumSetは、長い要素の配列をビットベクトルとして使用します。 これにより、この実装は64を超える要素を格納できます。 RegularEnumSet[とほぼ同じように機能します。 X205X]ただし、値が格納されている配列インデックスを見つけるためにいくつかの追加の計算を行います。

当然のことながら、配列の最初の長い要素には、 enum の最初の64個の値、次の64個の要素などが格納されます。

EnumSet ファクトリメソッドは、 enum の要素数に応じて、ある実装または別の実装のインスタンスを作成します。

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

コレクションに格納される要素の数ではなく、enumクラスのサイズのみが考慮されることに注意してください。

3.2. EnumSetを使用するメリット

上記で説明したEnumSetの実装により、 EnumSetのすべてのメソッドは、算術ビット演算を使用して実装されます。これらの計算は非常に高速であるため、すべての基本操作が実行されます。一定時間で実行されます。

EnumSetHashSetなどの他のSet実装と比較すると、値は予測可能な順序で格納され、1ビットのみが必要なため、通常は最初の方が高速です。計算ごとに調べます。 HashSet とは異なり、適切なバケットを見つけるためにhashcodeを計算する必要はありません。

さらに、ビットベクトルの性質により、EnumSetは非常にコンパクトで効率的です。 したがって、メモリの使用量が少なくなり、すべてのメリットが得られます。

4. 主な業務

EnumSet のメソッドの大部分は、インスタンスを作成するメソッドを除いて、他のSetと同じように機能します。

次のセクションでは、すべての作成メソッドを詳細に示し、残りのメソッドについて簡単に説明します。

この例では、 Color enumを使用します。

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

4.1. 作成方法

EnumSetを作成する最も簡単なメソッドはallOf()とnoneOf()です。このようにして、Colorのすべての要素を含むEnumSetを簡単に作成できます。列挙型:

EnumSet.allOf(Color.class);

同様に、 noneOf()を使用して反対のことを行い、Colorの空のコレクションを作成できます。

EnumSet.noneOf(Color.class);

列挙型要素のサブセットを使用してEnumSetを作成する場合は、オーバーロードされたof()メソッドを使用できます。 最大5つの異なるパラメーターの固定数を持つメソッドとvarargsを使用するメソッドを区別することが重要です。

Javadocによると、 varargs バージョンのパフォーマンスは、アレイが作成されているため、他のバージョンよりも遅くなる可能性があります。 したがって、最初に5つを超える要素を追加する必要がある場合にのみ使用する必要があります。

列挙型のサブセットを作成する別の方法は、 range()メソッドを使用することです。

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

上記の例では、EnumSetにはYellowからBlueまでのすべての要素が含まれています。これらはenumで定義された順序に従います。

[YELLOW, GREEN, BLUE]

指定された最初と最後の要素の両方が含まれていることに注意してください。

もう1つの便利なファクトリメソッドは、parameters として渡された要素を除外できるcomplementOf()です。 黒と白を除くすべてのColor要素を使用してEnumSetを作成しましょう。

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

このコレクションを印刷すると、他のすべての要素が含まれていることがわかります。

[RED, YELLOW, GREEN, BLUE]

最後に、別のEnumSetからすべての要素をコピーしてEnumSetを作成できます:

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

内部的には、cloneメソッドを呼び出します。

さらに、列挙型要素を含む任意のコレクションからすべての要素をコピーすることもできます。 これを使用して、リストのすべての要素をコピーしてみましょう。

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

この場合、listCopyには赤色のみが含まれます。

4.2. その他の操作

残りの操作は、他の Set 実装とまったく同じように機能し、使用方法に違いはありません。

したがって、空の EnumSet を簡単に作成し、いくつかの要素を追加できます。

EnumSet<Color> 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から入手できます。