티스토리 뷰

열거된 상수를 하나만 사용하는 것이 아닌 집합으로 관리하고 싶을 때, enum이 없던 시절 비트 필드를 사용했다.

public class Text{
    public static final int STYLED_BOLD = 1 <<0;
    public static final int STYLE_ITALIC = 1<<1;
    public static final int STYLE_UNDERLINE = 1<<2;
    public static final int STYLE_STRIKETHROUGH = 1 <<3;
    
    public void applyStyles(int styles) {...}
}

위의 비트들을 OR 연산을 통해 손쉽게 집합에 추가할 수 있다.

일단 비트 필드로 상수를 표현하는 것은 지금까지 얘기했던 정수형 상수 필드를 사용할 때의 문제를 그대로 가지고 있다. 그리고 출력했을 때 알아보기 힘들어 비트 파싱 로직을 개발자가 작성해야 한다. 또한 비트 마스킹의 고질적인 단점으로 32비트, 64비트 정수형을 넘어가면 저장할 수 없게 된다.

 

이런 고민을 모두 해결해 주기 위한 자료구조가 EnumSet이다.

    /**
     * Creates an empty enum set with the specified element type.
     *
     * @param <E> The class of the elements in the set
     * @param elementType the class object of the element type for this enum
     *     set
     * @return An empty enum set of the specified type.
     * @throws NullPointerException if {@code elementType} is null
     */
    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);
    }

EnumSet은 상수의 포함 여부를 비트를 통해 관리한다. enum type의 상수들은 선언 순서에 따라 하나에 인덱스에 매핑된다. (ordinal method) 따라서 이들의 인덱스를 통해 비트 필드에 저장할 수 있게 된다. enum의 상수 개수가 64개 이하인 경우에 RegularEnumSet으로 생성된다. RegularEnumSet의 경우엔 하나의 long에 상수 정보를 저장한다.

 

JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
    super(elementType, universe);
    elements = new long[(universe.length + 63) >>> 6];
}

예상과 달랐던 부분은 JumboEnumSet이다. 64비트 자료형에 마스킹 할 수 없으므로 그냥 HashSet과 같은 자료구조를 사용할 줄 알았는데 똑같이 비트 필드를 사용한다. long 배열을 통해 상수 정보를 관리한다. enum의 상수 개수에 63을 더하고 64로 나누어 총 필요한 배열의 길이를 구한다. enum이 인덱스가 연속된 상수의 모음이라는 점을 잘 이용한 것 같다.

코드를 까보다가 새로운 알고리즘 테크닉을 배웠다 ㄷㄷ;

 

 

비트를 파싱하거나, 추가하거나, 지우거나.. 근데 이걸 long 배열을 통해 관리하고.. 이걸 내가 만들어야 한다면 30일 정도 걸릴 것 같다. EnumSet 자료구조를 사용해 효과적으로 상수 집합을 표현할 수 있다.

 

// 코드 36-2 EnumSet - 비트 필드를 대체하는 현대적 기법 (224쪽)
public class Text {
    public enum Style {BOLD, ITALIC, UNDERLINE, STRIKETHROUGH}

    // 어떤 Set을 넘겨도 되나, EnumSet이 가장 좋다.
    public void applyStyles(Set<Style> styles) {
        System.out.printf("Applying styles %s to text%n",
                Objects.requireNonNull(styles));
    }

    // 사용 예
    public static void main(String[] args) {
        Text text = new Text();
        text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
    }
}

factory method인 of로 EnumSet의 인스턴스를 생성할 수 있다. 그런데 of 메소드가 상당히 다양했다. 인자를 한 개 받는 거, 두 개 받는 거, 세 개 받는 거, 네 개 받는 거... 가변 인수가 만들어지기 이전에 만든 메소드들인가 싶다.

 

 

Set과 같은 인터페이스를 파라미터로 받는다. EnumSet을 넣는게 성능상 이점이 있지만 인터페이스를 건네는 것이 좋은 습관이다. 미래에 EnumSet보다 좋은 자료구조가 만들어질 수도 있고, 정책이 변경될 수도 있다.

 

현재 EnumSet의 문제는 이 클래스가 mutable하다는 것이다. 자바 11에서 까지 아직 수정이 안 됐다고 한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함