peaches-book-study / effective-java

이펙티브 자바 3/E
0 stars 2 forks source link

Item 36. 비트 필드 대신 EnumSet을 사용하라 #39

Open hyunsoo10 opened 3 months ago

hyunsoo10 commented 3 months ago

Chapter : 6. 열거 타입과 애너테이션

Item : 36. 비트 필드 대신 EnumSet을 사용하라

Assignee : hyunsoo10


🍑 서론

열거한 값들이 주로 집합으로 사용될 경우, 예전에는 각 상수에 서로 다른 2의 거듭제곱 값을 할당한 정수 열거 패턴(아이템 34)을 사용해 왔다.

public class Text  {
     public static final int STYLE_BOLD            = 1 << 0;   // 1
     public static final int STYLE_ITALIC          = 1 << 1;   // 2
     public static final int STYLE_UNDERLINE       = 1 << 2;   // 4
     public static final int STYLE_STRIKETHROUGH   = 1 << 3;   // 8

    // 매개변수 styles는 0개 이상의 STYLE_ 상수를 비트별 OR한 값이다. 
    public void applyStyles(int styles)  { ... }

}

다음과 같은 식으로 비트별 OR를 사용해 여러 상수를 하나의 집합으로 모을 수 있는데, 이렇게 만들어진 집합을 비트 필드라한다.

text.applyStyles(STYLE_BOLD | STYLE_ITALIC);

비트 필드를 사용하면 비트별 연산을 사용해 합집합과 교집합 같은 집합 연산을 효율적으로 수행할 수 있지만, 비트 필드는 단순한 정수 열거 상수를 출력할 때보마 해석하기 훨씬 어렵다.

🍑 본론

java.util 패키지의 EnumSet 클래스는 열거 타입 상수의 값으로 구성된 집합을 효과적으로 표현해준다. Set 인터페이스를 완벽히 구현하며, 타입 안전하고, 다른 어떤 Set 구현체와도 함께 사용할 수 있다. EnumSet의 내부는 비트 벡터로 구현되어 있기 때문에 원소가 총 64개 이하라면 EnumSet 전체를 long 변수 하나로 표현하여 비트 필드에 비견되는 성능을 보여준다.

removeAll, retainAll과 같은 대량 작업은 비트를 효율적으로 처리할 수 있는 산술 연산을 써서 구현했다. 그러면서도 비트를 직접 다룰 때 흔히 겪는 오류들에서 해방된다.

import java.util.*;

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

    public void applyStyles(Set<Style> styles) {
        System.out.printf("Applying styles %s to text%n",
                styles);
    }

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

applyStyles 메서드가 EnumSet<Style>이 아닌 Set<Style>을 받은 이유는? -> 모든 클라이언트가 EnumSet을 건네리라 짐작되는 상황이라도 이왕이면 인터페이스로 받는게 일반적으로 좋은 습관이다(아이템64)

🍑 결론

열거할 수 있는 타입을 한데 모아 집합 형태로 사용한다고 해도 비트 필드를 사용할 이유는 없다. EnumSet 클래스가 비트 필드 수준의 명료함과 성능을 제공하고, 열거 타입의 장점까지 선사하기 때문이다. 유일한 단점이라면 불변 EnumSet을 만들 수 없다는 것이다.

필요하다면 Collections.unmodifiableSet으로 EnumSet을 감싸서 사용할 수 있다.


    enum Weekday {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }

    public static void main(String[] args) {

        // EnumSet 생성
        EnumSet<Weekday> weekdays = EnumSet.of(Weekday.MONDAY, Weekday.TUESDAY, Weekday.WEDNESDAY);

        // 불변 EnumSet 생성
        Set<Weekday> immutableWeekdays = Collections.unmodifiableSet(weekdays);

        // 불변 EnumSet에 요소 추가 또는 제거 시도
        immutableWeekdays.add(Weekday.THURSDAY);  // UnsupportedOperationException 발생
        immutableWeekdays.remove(Weekday.MONDAY);  // UnsupportedOperationException 발생

        // 불변 EnumSet 출력
        System.out.println("Immutable Weekdays: " + immutableWeekdays);
    }

Collections.unmodifiableSet() 메서드는 mutableSet을 수정할 수 없는 불변 Set으로 변환하는 메서드


Referenced by

-

heon118 commented 3 months ago

what? 에.. 전?