언어/Effective Java

이펙티브 자바 Item 36- 비트 필드 대신 EnumSet을 사용하라

now0204 2024. 6. 7. 11:08

 

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

 

public class StyleWithBitField {
    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

    public void applyStyles(int styles) {
//        styleWithBitField.applyStyles(STYLE_BOLD | STYLE_UNDERLINE);
    }
}

 

  • 위와 같이 비트별 OR를 사용해 여러 상수르 ㄹ하나의 집합으로 모을 수 있으며, 이 집합을 비트 필드라한다.
  • 하지만, 비트 필드 또한 정수 열거 상수이므로, 정수 열거 상수의 단점을 그대로 지니며, 추가로 다른 문제도 가진다.
    • 비트 필드 값이 그대로 출력되면, 단순한 정수 열거 상수를 출력할 때보다 해석하기 더 어렵다
    • 비트 필드 하나에 녹아 있는 모든 원소를 순회하기 까다롭다.
    • 최대 몇 비트가 필요한지 처음부터 예상하고 적절한 타입을 선택해야한다.

EnumSet

 

  • 열거 타입 상수의 값으로 구성된 집합을 효과적으로 표현해준다.
    • EnumSet클래스는 Set 인터페이스를 구현하여 어떤 Set 구현체와도 함께 사용할 수 있으며, 타입 안전하다.
    • EnumSet 내부는 비트 벡터로 구현되어, 원소가 총 64개 이하라면, EnumSet 전체를 long 변수 하나로 표현하여 비트필드에 비견되는 성능을 보여준다. 
    • removeAll과 retainAll과 같은 대량 작업은 비트를 효율적으로 처리할 수 있는 산술 연산을 써서 구현했다.
import java.util.EnumSet;
import java.util.Set;

public class Text {

    public void applyStyles(Set<Style> styles) {
//        applyStyles(EnumSet.of(Style.BOLD, Style.UNDERLINE));
    }

    private enum Style {
        BOLD, ITALIC, UNDERLINE, STRIKETHROUGH
    }

 

단점

  • EnumSet을 불변으로 반들 수 없다.
  • 성능상 손해를 보게되지만, Collections.numodifiableSet()메서드를 통해 EnumSet을 불변으로 만들 수 있다.

 


비트 필드란?

 

메모리를 절약하기 위한 도구로 사용되었다. 비트 필드는 참-거짓 값을 여럿 결합해서 하나의 바이트로 만들어 메모리를 절약한다. 

public class Car {
 public final static int POWER_WINDOWS = 1;
 public final static int POWER_LOCKS = 2;
 public final static int SNOW_TIRES = 4;
 public final static int STANDARD_TIRES = 8;
 public final static int CRUISE_CONTROL = 16;
 public final static int CD_PLAYER = 32;
 public final static int AIR_CONDITIONING = 64;
 private int options;
 public Car( ) {
 }
 public void setOptions(final int options) {
 this.options = options;
 }
 public int getOptions( ) {
 return this.options;
 }
}
  • 각 비트마다 1혹은 0으로 옵션의 유무를 나타내게 된다.
  • 위 필드에 저장된 값으로, 연산을 통해 car가 가진 특징들을 특정할 수 있는 것이다. 

비트 어레이 vs 비트 필드

 

  1. 비트 어레이 : 정수 타입보다 크기가 크고, 정수로 인덱스된 많은 비트 집합을 저장하는데 사용
  2. 비트 필드 : 크기가 워드를 넘지 않는다. 

비트 필드의 사용

  • 비트 빌트 대체 : OR연산을 통해 나온 합이 옵션을 알려준다. 또한 XOR 연산을 이용해 옵션을 제거할 수도 있다.
  • 비트 필드 비교 : AND 연산을 통해 두 비트연산자를 비교할 수 있다. 만약 결과가 1이라면 차는 해당 옵션을 가지고 있다. 

EnumSet 이란?

 

EnumSet은 java.util 패키지에서 제공하는 Enum클래스와 함께 동작하는 구현체이다.

상수 그룹을 나타내는 목적으로 사용되는데, 예를 들어 봄,여름,가을,겨울 등 열거형을 나열하는 것으로 볼 수 있다. 

 

특징

  • enum값만 포함할 수 있고, 모든 값은 동일한 enum에 속해야한다.
  • null 값을 추가하는 것은 허용하지 않는다.
  • thread로부터 안전하지 않다.! 필요한 경우 외부에서 동기화해야한다.
  • HashSet보다 훨씬 빠른 고성능 구현체다.
  • iterator를 사용한 복제가 가능하다.
  • EnumSet의 모든 메서드는 산술 비트 연산자로 구현되어 있다.

 

사용법

 

EnumSet static 메서드

  • EnumSet.allOf() : 매개변수로 받는 열거형에 있는 모든 요소를 가져온다
  • Enum.of() : 매개변수로 받은 해당 요소를 찾아서 넣는다.
  • Enum.complementOf() : 매개변수로 받은 요소들만 빼고 넣는다.
  • EnumSet.range() : 매개변수로 받는 두개의 구간안에 해당하는 요소를 넣는다.
  • EnumSet.noneOf() : 매개변수로 받는 열거형을 비운 EnumSet을 반환한다. 
enum Color {
    RED, YELLOW, GREEN, BLUE, BLACK, WHITE
}
public class EnumDemo {
    public static void main(String[] args) {
        EnumSet<Color> set1, set2, set3, set4, set4_1, set5;

        set1 = EnumSet.allOf(Color.class);
        set2 = EnumSet.of(Color.RED, Color.GREEN, Color.BLUE);
        set3 = EnumSet.complementOf(set2);
        set4 = EnumSet.range(Color.YELLOW, Color.BLACK);
        set5 = EnumSet.noneOf(Color.class);

        set5.add(Color.BLACK);
        set5.add(Color.BLUE);
        set5.remove(Color.BLACK);

        System.out.println("set1 = " + set1);
        System.out.println("set2 = " + set2);
        System.out.println("set3 = " + set3);
        System.out.println("set4 = " + set4);
        try {
            set4_1 = EnumSet.range(Color.BLACK, Color.YELLOW);
            System.out.println("set4_1 = " + set4_1);
        } catch (IllegalArgumentException e) {
            System.out.println("set4_1은 오류가 발생합니다 : " + e.getMessage());
        }
        System.out.println("set5 = " + set5);
    }
}

 


참고자료 

https://www.tcpschool.com/c/c_operator_bitwise

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

https://javabom.tistory.com/50

 

[아이템 36] 비트 필드 대신 EnumSet을 사용하라

열거한 값들이 주로 집합으로 사용될 경우, 예전에는 상수에 서로 다른 2의 거듭제곱 값을 할당한 정수 열거 패턴을 사용해왔다. package Chap5_EnumTypeAndAnnotation.item36; public class StyleWithBitField { public s

javabom.tistory.com

https://ksabs.tistory.com/199

 

[Java] Enum

enum 정의하는 방법 enum이 제공하는 메소드 (values()와 valueOf()) java.lang.Enum EnumSet Enum 열거형(enum)은 서로 관련된 상수를 편리하게 선언하기 위해 JDK1.5부터 새로 추가되었습니다. Enum 정의하는 방법 e

ksabs.tistory.com