EnumSet 파헤치기

enum 타입의 컬렉션은 EnumSet을 활용하자

Posted on 2019-12-05

EnumSet 파헤치기


특징

EnumSetenum 클래스로 동작하는 특수한 Set 컬렉션으로 대부분의 메서드를 재정의한다.

  • enum 타입만 가질 수 있으며 모든 값은 동일한 enum에 속해야 한다.
  • null 값을 추가하면 NullPointerException 이 발생한다.
  • thread-safe 하지 않으므로 필요한 경우 외부에서 동기화해야 한다.
  • 요소들은 enum에 선언 된 순서대로 저장된다.
  • 복사본에서 fail-safe iterator를 사용하므로 컬렉션을 반복할 때 컬렉션이 수정되더라도 ConcurrentModificationException 이 발생하지 않는다.

왜 사용하는가

enum 값을 저장할 때 다른 Set 구현체보다 EnumSet 을 우선시해야 한다.

1) 구현 세부사항

  • EnumSet 은 인스턴스를 생성 할 수있는 여러 정적 팩토리 메소드를 포함하는 추상 클래스다.
  • JDK는 비트 벡터에 의해 지원되는 두 가지의 구현체 RegularEnumSetJumboEnumSet 을 제공한다.
    • RegularEnumSet
      • long을 사용하여 비트 벡터를 나타내고, 요소의 각 비트는 enum의 값을 나타낸다.
      • long은 64비트 데이터 타입이므로 최대 64개의 요소만 저장할 수 있다.
      • enum 값의 순서대로 저장되어 인덱스를 바로 찾을 수 있다.
    • JumboEnumSet
      • long 배열을 사용하여 비트 벡터로 나타내고,
      • long 배열 타입이므로 64개 이상의 요소를 저장할 수 있다.
      • 단, 값이 저장된 배열 인덱스를 찾기 위해 추가 계산을 수행해야 한다.
  • EnumSet 팩토리 메소드는 enum 의 요소 수에 맞는 구현체로 인스턴스를 생성한다. (컬렉션에 저장 될 요소의 개수가 아니라 enum 클래스 요소의 수로 결정한다.)
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
    Enum<?>[] universe = getUniverse(elementType);
    if (universe == null)
        throw new ClassCastException(elementType + " not an enum");

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

2) 장점

  • EnumSet모든 메서드는 산술 비트 연산을 사용하여 구현되므로 연산 속도가 매우 빨라 상수 시간내에 실행된다.
  • EnumSet 을 다른 Set 구현체와 비교하면 값이 예측 가능한 순서로 저장되고 각 계산마다 1비트만 검사하면 되기때문에 일반적으로 더 빠르다. HashSet과 달리 버킷을 찾기 위해 해시 코드를 계산할 필요가 없다.
  • 비트 벡터의 특성으로 인해 EnumSet 은 매우 작고 효율적이므로 메모리를 적게 사용하면서 모든 이점을 얻을 수 있다.

주요 메서드


public enum DayOfWeek {
  MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

// 비어있는 생성자
// :
EnumSet<DayOfWeek> none = EnumSet.noneOf(DayOfWeek.class);

// 모든 요소를 지닌 생성자 
// : MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY
EnumSet<DayOfWeek> everyday = EnumSet.allOf(DayOfWeek.class);

// 특정 요소로만 이루어진 생성자 
// : SATURDAY SUNDAY
EnumSet<DayOfWeek> weekend = EnumSet.Of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);

// 모든 요소 중에 전달받은 요소들을 제외한 생성자
// : MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY
EnumSet<DayOfWeek> weekday = EnumSet.complementOf(weekend);

// enum에 선언된 순서의 범위로 이루어진 생성자 
// : MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY
EnumSet<DayOfWeek> weekday = EnumSet.range(DayOfWeek.MONDAY, DayOfWeek.FRIDAY);


// 포함
assertThat(weekday.contains(DayOfWeek.WEDNESDAY)).isTrue();
assertThat(weekday.contains(DayOfWeek.SUNDAY)).isFalse();

// 추가
weekday.add(DayOfWeek.SUNDAY);
assertThat(weekday.contains(DayOfWeek.SUNDAY)).isTrue();

// 삭제
weekday.remove(DayOfWeek.WEDNESDAY);
assertThat(weekday.contains(DayOfWeek.WEDNESDAY)).isFalse();

// 복사
List<DayOfWeek> list = new ArrayList<>();
list.add(DayOfWeek.SATURDAY);
list.add(DayOfWeek.SUNDAY);
EnumSet<DayOfWeek> listCopy = EnumSet.copyOf(list);
assertThat(listCopy.containsAll(weekend)).isTrue();



Reference