36. 비트 필드 대신 EnumSet을 사용하라
핵심 요약
열거 타입 상수의 집합을 표현할 때, 과거에 사용되던 비트 필드(Bit Field) 방식 사용했습니다.
현대 자바에서는 EnumSet
을 사용하는 것이 훨씬 안전하고 효율적이며 가독성 높은 방법입니다.
EnumSet
은 타입 안전성을 보장하고 비트 필드 수준의 성능을 제공하면서도 사용하기 쉽습니다.
비트 필드
정의
열거한 값들을 주로 집합으로 사용할 경우, 각 상수에 서로 다른 2의 거듭제곱 값을 할당한 정수 열거 패턴을 사용
비트별 OR(
|
) 연산을 통해 여러 상수를 하나의 집합(정수 값)으로 표현
예시 코드
public class FilePermissions {
public static final int PERMISSION_READ = 1 << 0; // 1 (읽기 권한)
public static final int PERMISSION_WRITE = 1 << 1; // 2 (쓰기 권한)
public static final int PERMISSION_EXECUTE = 1 << 2; // 4 (실행 권한)
public void setPermissions(int permissions) {...}
}
// 상수 정의 (정수 값 사용)
public class Text_BitField {
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
// 비트 필드(int)를 받는 메서드
public void applyStyles(int styles) {
System.out.println("Applying styles (bitmask): " + styles);
// ... 스타일 적용 로직 ...
}
}
// 사용 예시
text.applyStyles(STYLE_ITALIC | STYLE_UNDERLINE); // 값은 6
fp.setFontSize(STYLE_ITALIC | STYLE_UNDERLINE); // 오류없음
특징
비트별 연산(OR, AND 등)을 사용하여 합집합, 교집합 등의 집합 연산을 효율적으로 수행 가능 (과거 관점).
장점
(과거 기준) 비트 연산을 통한 효율적인 집합 연산.
단점
타입 안전성 부재
단순
int
값이므로 관련 없는 값을 전달해도 컴파일 오류 미발생
가독성 저하
숫자만으로 스타일 정보 추론?
순회 어려움
집합에 포함된 개별 원소(스타일)를 순회하기 까다로움
확장성 제한
최대 원소 개수를 미리 예측하여
int
나long
타입을 선택비트 수 확장할 때 API 변경이 필요
오류 가능성
비트 연산은 직접 다루기 까다롭고 오류가 발생 가능성
EnumSet
정의
열거 타입(Enum) 상수들로 구성된 집합을 표현하기 위해 특별히 설계된
Set
인터페이스의 고성능 구현체Set 인터페이스를 완벽히 구현, 타입 안전하고, 다른 어떤 Set 구현체와도 함께 사용 가능
비트 필드를 대체하는 현대적이고 우수한 대안
java.util
패키지
예시 코드
import java.util.EnumSet;
import java.util.Set;
// 열거 타입 정의
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
public class Text_EnumSet {
// Set 인터페이스로 받는 것이 좋음
public void applyStyles(Set<Style> styles) {
System.out.println("Applying styles (EnumSet): " + styles);
// ... 스타일 적용 로직 ...
}
}
// 사용 예시
Text_EnumSet text = new Text_EnumSet();
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC)); // 출력: [BOLD, ITALIC]
특징
내부적으로 비트 벡터를 사용하여 데이터를 저장
비트 벡터는 주로 단일
long
변수매우 효율적
Set
인터페이스를 완벽하게 구현하며, 다른Set
구현체와 함께 사용 가능다양한 정적 팩터리 메서드를 제공하여 편리한 인스턴스 생성
EnumSet.of()
,EnumSet.allOf()
,EnumSet.noneOf()
등
장점
타입 안전성
정의된 열거 타입의 상수만 포함
컴파일 시점에 타입 오류를 방지
가독성
코드가 명확, 직관적
출력 예시
[BOLD, ITALIC]
고성능
내부 구현 방식 덕분에 비트 필드에 비견될 만한 성능을 제공
대량 작업도 비트 연산을 활용해 효율적으로 처리
removeAll, retainAll 등
사용 편의성
복잡한 비트 연산 내부 구현
안전하고 편리한 집합 연산 제공
유지보수 용이
열거 타입 상수 추가시
EnumSet
코드는 대부분 수정 없이 작동
단점
(과거) 불변
EnumSet
을 직접 생성 불가 (Java 9 이전)현재는 해결
핵심 정리 및 부가 정보
핵심 정리
결론적으로, 열거 타입 상수의 집합을 사용할 때는 명료성, 성능, 타입 안전성, 유지보수성 등 모든 면에서 비트 필드 대신
EnumSet
을 사용하는 것이 압도적으로 좋습니다.
API 설계 팁:
// 구체화 EnumSet<E> -> 인터페이스 Set<E>
public void applyStyles(Set<Style> styles) // EnumSet ?
EnumSet<E>
대신Set<E>
을 사용하는 것이 더 유연하고 좋은 설계클라이언트가 다른
Set
구현체를 사용할 가능성
불변성(Immutability) 처리:
Collections.unmodifiableSet(EnumSet.of(...))
Java 9 이전에는 불변
EnumSet
을 만드는 표준적인 방법이 없어서 사용원본
EnumSet
이 변경되면 같이 변경될 수 있는 '뷰'를 제공할 뿐명확성, 성능 조금 희생
Set.copyOf(enumSet)
Java 10 이후 도입
해당
EnumSet
의 내용을 복사하여 진정한 불변Set
을 생성더 안전하고, 특히
EnumSet
의 경우 내부적으로 최적화되어 성능상 이점
Last updated