23 KiB
BitSet 클래스란?
BitSet은 비트(bit) 단위의 데이터를 효율적으로 저장하고 조작할 수 있도록 설계된 클래스이다.
기본적인 boolean[] 배열과 유사하지만, 각 요소를 1비트로 저장하기 때문에 메모리를 훨씬 적게 사용한다.
이 클래스는 대량의 불리언 데이터를 다룰 때 유용하며, 비트 연산을 활용하는 알고리즘(예: 비트마스크, 소수 판별, 집합 연산 등)에서도 사용된다.
1. BitSet의 주요 특징
✔ 1비트 단위로 저장 → boolean[]보다 메모리 절약
✔ 자동 확장 가능 → 크기를 명시할 필요 없음
✔ 비트 연산 지원 → AND, OR, XOR 등 논리 연산 가능
✔ 빠른 검색 → 특정 비트의 ON/OFF를 빠르게 확인 가능
2. BitSet의 주요 메서드 정리
| 메서드 | 설명 |
|---|---|
BitSet() |
빈 BitSet 생성 (기본 크기 64비트) |
BitSet(int nbits) |
최소 nbits 크기의 BitSet 생성 |
set(int index) |
특정 위치의 비트를 1(true)로 설정 |
set(int fromIndex, int toIndex) |
특정 범위의 비트를 1(true)로 설정 |
clear(int index) |
특정 위치의 비트를 0(false)로 설정 |
clear(int fromIndex, int toIndex) |
특정 범위의 비트를 0(false)로 설정 |
flip(int index) |
특정 위치의 비트를 반전 |
flip(int fromIndex, int toIndex) |
특정 범위의 비트를 반전 |
get(int index) |
특정 위치의 비트 값을 반환 (true/false) |
get(int fromIndex, int toIndex) |
특정 범위의 비트 값을 BitSet으로 반환 |
and(BitSet set) |
AND 연산 수행 |
or(BitSet set) |
OR 연산 수행 |
xor(BitSet set) |
XOR 연산 수행 |
cardinality() |
1(true)로 설정된 비트의 개수 반환 |
length() |
가장 높은 1(true) 비트의 인덱스 + 1 반환 |
size() |
내부 비트 배열의 전체 크기 반환 |
isEmpty() |
모든 비트가 0(false)인지 확인 |
nextSetBit(int index) |
index 이후 첫 번째 1(true) 비트의 위치 반환 |
nextClearBit(int index) |
index 이후 첫 번째 0(false) 비트의 위치 반환 |
3. BitSet 사용 예제
(1) 기본적인 비트 설정 및 조회
import java.util.BitSet;
public class BitSetExample {
public static void main(String[] args) {
BitSet bitSet = new BitSet(); // 기본 크기 64비트
bitSet.set(0); // 0번째 비트 ON
bitSet.set(2); // 2번째 비트 ON
bitSet.set(4); // 4번째 비트 ON
System.out.println("BitSet: " + bitSet); // {0, 2, 4}
// 특정 비트 확인
System.out.println("0번째 비트: " + bitSet.get(0)); // true
System.out.println("1번째 비트: " + bitSet.get(1)); // false
}
}
✔ BitSet을 출력하면 1(true)인 비트의 위치만 표시된다.
(2) flip()을 활용한 비트 반전
import java.util.BitSet;
public class BitSetFlipExample {
public static void main(String[] args) {
BitSet bitSet = new BitSet();
bitSet.set(1);
bitSet.set(3);
bitSet.set(5);
System.out.println("초기 BitSet: " + bitSet); // {1, 3, 5}
bitSet.flip(0, 6); // 0~5까지 비트 반전
System.out.println("flip(0,6) 후: " + bitSet); // {0, 2, 4}
}
}
✔ flip()을 사용하면 지정된 범위의 비트가 반전된다.
(3) 비트 연산 (AND, OR, XOR)
import java.util.BitSet;
public class BitSetOperations {
public static void main(String[] args) {
BitSet set1 = new BitSet();
BitSet set2 = new BitSet();
set1.set(0);
set1.set(1);
set1.set(2);
System.out.println("Set1: " + set1); // {0, 1, 2}
set2.set(1);
set2.set(2);
set2.set(3);
System.out.println("Set2: " + set2); // {1, 2, 3}
BitSet andSet = (BitSet) set1.clone();
andSet.and(set2);
System.out.println("AND 연산: " + andSet); // {1, 2}
BitSet orSet = (BitSet) set1.clone();
orSet.or(set2);
System.out.println("OR 연산: " + orSet); // {0, 1, 2, 3}
BitSet xorSet = (BitSet) set1.clone();
xorSet.xor(set2);
System.out.println("XOR 연산: " + xorSet); // {0, 3}
}
}
✔ AND 연산: 1(true)인 비트만 유지
✔ OR 연산: 하나라도 1(true)이면 유지
✔ XOR 연산: 1(true)가 서로 다르면 유지
(4) cardinality(), nextSetBit(), nextClearBit() 활용
import java.util.BitSet;
public class BitSetMethods {
public static void main(String[] args) {
BitSet bitSet = new BitSet();
bitSet.set(1);
bitSet.set(3);
bitSet.set(5);
System.out.println("BitSet: " + bitSet); // {1, 3, 5}
System.out.println("1의 개수: " + bitSet.cardinality()); // 3
System.out.println("다음 1(true) 비트 위치: " + bitSet.nextSetBit(0)); // 1
System.out.println("다음 0(false) 비트 위치: " + bitSet.nextClearBit(0)); // 0
}
}
✔ cardinality()는 1(true)의 개수를 반환
✔ nextSetBit()는 다음 1(true)인 비트 위치 반환
✔ nextClearBit()는 다음 0(false)인 비트 위치 반환
4. BitSet vs. boolean 배열
| 비교 항목 | BitSet |
boolean[] |
|---|---|---|
| 메모리 사용 | 1비트(압축됨) | 1바이트(8배 큼) |
| 크기 조정 | 자동 확장 | 고정 크기 |
| 비트 연산 | AND, OR, XOR 지원 | 직접 구현 필요 |
| 성능 | 비트 연산이 빠름 | 단순한 경우 유리 |
✔ BitSet은 메모리를 절약하면서도 대량의 비트 연산을 효율적으로 수행할 수 있다.
✔ 다만, 단순한 배열이 필요하다면 boolean[]이 더 직관적일 수 있다.
5. 결론
BitSet은 대량의 불리언 데이터를 비트 단위로 저장하고 처리하는 데 유용하며,
비트 연산이 필요한 비트마스크, 소수 판별, 집합 연산, 압축 데이터 처리 등에 많이 활용된다.
단순한 boolean[]보다 메모리를 절약하면서도 연산 속도가 빠르다는 점이 강점이다.
BitSet 클래스의 메서드 정리 및 설명
자바의 BitSet 클래스는 java.util 패키지에 포함된 클래스로, 비트 단위로 데이터를 효율적으로 관리하기 위한 자료 구조입니다. 이는 이진수 데이터를 다룰 때 메모리를 절약하며, 비트 연산(AND, OR, XOR 등)을 쉽게 수행할 수 있도록 설계되었습니다. BitSet은 가변 크기의 비트 배열로 동작하며, 인덱스는 0부터 시작합니다.
아래에서 BitSet의 주요 메서드를 표로 정리하고, 각 메서드의 사용법과 예시를 설명하겠습니다.
BitSet 클래스 메서드 표
| 메서드명 | 반환 타입 | 설명 |
|---|---|---|
BitSet() |
- | 빈 BitSet 객체를 생성 (기본 크기 64비트) |
BitSet(int nbits) |
- | 지정된 초기 크기(nbits)로 BitSet 생성 |
set(int bitIndex) |
void |
지정된 인덱스의 비트를 true(1)로 설정 |
set(int bitIndex, boolean value) |
void |
지정된 인덱스의 비트를 주어진 값(true 또는 false)으로 설정 |
set(int fromIndex, int toIndex) |
void |
지정된 범위(fromIndex부터 toIndex-1까지)의 비트를 true로 설정 |
clear(int bitIndex) |
void |
지정된 인덱스의 비트를 false(0)로 설정 |
clear(int fromIndex, int toIndex) |
void |
지정된 범위의 비트를 false로 설정 |
clear() |
void |
모든 비트를 false로 설정 |
get(int bitIndex) |
boolean |
지정된 인덱스의 비트 값을 반환 |
get(int fromIndex, int toIndex) |
BitSet |
지정된 범위의 비트를 포함하는 새로운 BitSet 반환 |
flip(int bitIndex) |
void |
지정된 인덱스의 비트를 반전 (true ↔ false) |
flip(int fromIndex, int toIndex) |
void |
지정된 범위의 비트를 반전 |
and(BitSet set) |
void |
현재 BitSet과 주어진 BitSet 간 비트 단위 AND 연산 수행 |
or(BitSet set) |
void |
현재 BitSet과 주어진 BitSet 간 비트 단위 OR 연산 수행 |
xor(BitSet set) |
void |
현재 BitSet과 주어진 BitSet 간 비트 단위 XOR 연산 수행 |
andNot(BitSet set) |
void |
현재 BitSet에서 주어진 BitSet의 비트를 제거 (AND NOT 연산) |
cardinality() |
int |
true인 비트의 개수 반환 |
length() |
int |
마지막 true 비트의 인덱스 + 1 반환 (논리적 크기) |
size() |
int |
내부적으로 할당된 비트 수 반환 (물리적 크기, 64의 배수) |
isEmpty() |
boolean |
모든 비트가 false인지 확인 |
intersects(BitSet set) |
boolean |
주어진 BitSet과 공통된 true 비트가 있는지 확인 |
nextSetBit(int fromIndex) |
int |
지정된 인덱스 이후 첫 번째 true 비트의 인덱스 반환, 없으면 -1 |
nextClearBit(int fromIndex) |
int |
지정된 인덱스 이후 첫 번째 false 비트의 인덱스 반환 |
previousSetBit(int fromIndex) |
int |
지정된 인덱스 이전의 마지막 true 비트의 인덱스 반환, 없으면 -1 |
previousClearBit(int fromIndex) |
int |
지정된 인덱스 이전의 마지막 false 비트의 인덱스 반환 |
toByteArray() |
byte[] |
BitSet을 바이트 배열로 변환 |
toLongArray() |
long[] |
BitSet을 long 배열로 변환 |
valueOf(byte[] bytes) |
BitSet |
바이트 배열에서 BitSet 생성 (정적 메서드) |
valueOf(long[] longs) |
BitSet |
long 배열에서 BitSet 생성 (정적 메서드) |
clone() |
Object |
BitSet의 복사본 반환 |
equals(Object obj) |
boolean |
주어진 객체와 동일한지 비교 |
toString() |
String |
true 비트의 인덱스를 포함한 문자열 반환 (예: "{0, 2, 4}") |
BitSet 설명 및 예시
BitSet은 비트 단위로 데이터를 표현하므로 메모리 효율성이 높고, 비트 연산이 필요한 경우 유용합니다. 주요 사용 사례는 플래그 관리, 집합 표현, 데이터 압축 등입니다. 아래에서 주요 메서드의 사용법을 예시로 설명합니다.
1. 비트 설정 및 확인
비트를 설정하고 값을 확인합니다.
import java.util.BitSet;
public class BitSetBasicExample {
public static void main(String[] args) {
BitSet bits = new BitSet();
bits.set(0); // 0번 비트 = true
bits.set(2); // 2번 비트 = true
bits.set(4, 6); // 4~5번 비트 = true
System.out.println("Bit at 0: " + bits.get(0)); // true
System.out.println("Bit at 1: " + bits.get(1)); // false
System.out.println("Bits: " + bits); // {0, 2, 4, 5}
}
}
- 출력:
Bit at 0: true Bit at 1: false Bits: {0, 2, 4, 5}
2. 비트 연산
and, or, xor 연산을 수행합니다.
import java.util.BitSet;
public class BitSetOperationExample {
public static void main(String[] args) {
BitSet bits1 = new BitSet();
bits1.set(0);
bits1.set(1);
BitSet bits2 = new BitSet();
bits2.set(1);
bits2.set(2);
BitSet andResult = (BitSet) bits1.clone();
andResult.and(bits2);
System.out.println("AND: " + andResult); // {1}
BitSet orResult = (BitSet) bits1.clone();
orResult.or(bits2);
System.out.println("OR: " + orResult); // {0, 1, 2}
BitSet xorResult = (BitSet) bits1.clone();
xorResult.xor(bits2);
System.out.println("XOR: " + xorResult); // {0, 2}
}
}
- 출력:
AND: {1} OR: {0, 1, 2} XOR: {0, 2}
3. 비트 탐색
다음 true 또는 false 비트를 찾습니다.
import java.util.BitSet;
public class BitSetSearchExample {
public static void main(String[] args) {
BitSet bits = new BitSet();
bits.set(2);
bits.set(5);
int nextSet = bits.nextSetBit(0); // 2
int nextClear = bits.nextClearBit(2); // 3
System.out.println("Next set bit from 0: " + nextSet);
System.out.println("Next clear bit from 2: " + nextClear);
}
}
- 출력:
Next set bit from 0: 2 Next clear bit from 2: 3
4. 바이트 배열 변환
BitSet을 바이트 배열로 변환하고 역변환합니다.
import java.util.BitSet;
public class BitSetToByteExample {
public static void main(String[] args) {
BitSet bits = new BitSet();
bits.set(0);
bits.set(3);
bits.set(7);
byte[] byteArray = bits.toByteArray();
System.out.print("Byte array: ");
for (byte b : byteArray) {
System.out.printf("%02X ", b); // 09 80 (00001001 10000000)
}
System.out.println();
BitSet fromBytes = BitSet.valueOf(byteArray);
System.out.println("From bytes: " + fromBytes); // {0, 3, 7}
}
}
- 출력:
Byte array: 09 80 From bytes: {0, 3, 7}
5. 상태 확인
BitSet의 크기와 개수를 확인합니다.
import java.util.BitSet;
public class BitSetStatusExample {
public static void main(String[] args) {
BitSet bits = new BitSet();
bits.set(2);
bits.set(5);
System.out.println("Cardinality: " + bits.cardinality()); // 2
System.out.println("Length: " + bits.length()); // 6
System.out.println("Size: " + bits.size()); // 64
System.out.println("Is empty: " + bits.isEmpty()); // false
}
}
- 출력:
Cardinality: 2 Length: 6 Size: 64 Is empty: false
BitSet의 특징과 주의점
- 장점:
- 메모리 효율성:
true비트만 저장하며, 필요한 만큼 동적으로 확장. - 비트 연산 지원: 집합 연산(합집합, 교집합 등)을 쉽게 구현.
- 메모리 효율성:
- 주의점:
- 인덱스 음수 불가:
IllegalArgumentException발생. - 동기화 없음: 멀티스레드 환경에서
Collections.synchronizedSet으로 감싸야 함. - 크기:
size()는 물리적 크기(64비트 단위),length()는 논리적 크기.
- 인덱스 음수 불가:
결론
BitSet은 비트 단위 작업을 효율적으로 처리하며, 설정(set), 확인(get), 연산(and, or, xor), 변환(toByteArray) 등 다양한 기능을 제공합니다. 위 예시를 통해 비트 관리, 집합 연산, 데이터 변환 등에 활용해 보세요! 필요 시 크기 조정이나 동기화 처리를 추가해 사용하면 더욱 강력한 도구가 됩니다.
BitSet의 인덱스 순서에 대한 설명
자바의 BitSet 클래스는 비트 단위로 데이터를 관리하는 자료 구조로, 각 비트는 0부터 시작하는 정수 인덱스에 의해 식별됩니다. 이 인덱스 순서는 비트의 위치를 나타내며, BitSet의 동작 방식과 데이터 표현에 중요한 영향을 미칩니다. 아래에서 BitSet의 인덱스 순서에 대해 자세히 설명하고, 예시를 통해 이해를 돕겠습니다.
1. 인덱스 순서의 기본 개념
BitSet의 인덱스: 0부터 시작하는 정수로, 비트의 위치를 나타냅니다.- 인덱스 0은 가장 낮은 비트(least significant bit, LSB)를 의미하며, 인덱스가 증가할수록 더 높은 비트(most significant bit, MSB)로 이동합니다.
- 순서: 논리적으로 왼쪽에서 오른쪽으로 증가하는 순서가 아니라, 내부적으로는 오른쪽에서 왼쪽으로 비트가 배치됩니다. 하지만 사용자가 접근할 때는 인덱스 0이 시작점으로 간주됩니다.
- 크기:
BitSet은 필요에 따라 동적으로 확장되며, 초기 크기는 64비트 단위로 할당됩니다.
비트와 인덱스 매핑 예시
- 8비트 기준으로 생각하면:
인덱스: 7 6 5 4 3 2 1 0 비트: [0, 0, 0, 0, 1, 0, 1, 0] // 10진수 10- 인덱스 1이
true이고, 인덱스 3이true이면 값은00001010(10진수로 10).
- 인덱스 1이
2. 인덱스 순서와 메서드 동작
BitSet의 메서드는 이 인덱스 순서를 기반으로 동작합니다. 주요 메서드와 인덱스 순서의 관계를 살펴보겠습니다.
set(int bitIndex)와 get(int bitIndex)
set(3)은 인덱스 3의 비트를true로 설정하며, 이는 오른쪽에서 네 번째 비트에 해당합니다.get(3)은 인덱스 3의 비트 값을 반환합니다.
nextSetBit(int fromIndex)와 previousSetBit(int fromIndex)
nextSetBit(0)은 인덱스 0부터 시작해 첫 번째true비트의 인덱스를 반환합니다. 순서는 낮은 인덱스에서 높은 인덱스로 진행.previousSetBit(5)는 인덱스 5 이전의 마지막true비트의 인덱스를 반환하며, 높은 인덱스에서 낮은 인덱스로 역방향 탐색.
toByteArray()와 바이트 순서
BitSet을 바이트 배열로 변환할 때, 낮은 인덱스가 낮은 바이트에 먼저 매핑됩니다.- 예: 인덱스 0
7이 첫 번째 바이트, 815가 두 번째 바이트로 변환. - 바이트 내부에서는 비트가 LSB에서 MSB로 배치됩니다 (즉, 인덱스 0이 바이트의 최하위 비트).
- 예: 인덱스 0
3. 인덱스 순서 예시
예시 1: 비트 설정과 확인
import java.util.BitSet;
public class BitSetIndexOrder {
public static void main(String[] args) {
BitSet bits = new BitSet();
bits.set(0); // 인덱스 0 = true
bits.set(3); // 인덱스 3 = true
bits.set(7); // 인덱스 7 = true
System.out.println("Bits: " + bits); // {0, 3, 7}
System.out.println("Bit at 0: " + bits.get(0)); // true
System.out.println("Bit at 1: " + bits.get(1)); // false
}
}
- 출력:
Bits: {0, 3, 7} Bit at 0: true Bit at 1: false - 설명: 인덱스 0, 3, 7이
true로 설정되며, 이는 8비트 기준으로10001001(10진수 137)과 같습니다.
예시 2: 바이트 배열 변환
import java.util.BitSet;
public class BitSetToByteOrder {
public static void main(String[] args) {
BitSet bits = new BitSet();
bits.set(0); // 00000001
bits.set(3); // 00001000
bits.set(7); // 10000000
byte[] bytes = bits.toByteArray();
for (byte b : bytes) {
System.out.printf("%02X ", b); // 09 (00001001)
}
System.out.println();
}
}
- 출력:
09 - 설명:
- 인덱스 0~7이 첫 번째 바이트로 변환.
00001001(인덱스 7이 MSB, 인덱스 0이 LSB) = 16진수09.
예시 3: 비트 탐색
import java.util.BitSet;
public class BitSetSearchOrder {
public static void main(String[] args) {
BitSet bits = new BitSet();
bits.set(2);
bits.set(5);
bits.set(10);
int next = bits.nextSetBit(0); // 2
int nextFrom3 = bits.nextSetBit(3); // 5
int prevFrom10 = bits.previousSetBit(10); // 10
System.out.println("Next set from 0: " + next);
System.out.println("Next set from 3: " + nextFrom3);
System.out.println("Previous set from 10: " + prevFrom10);
}
}
- 출력:
Next set from 0: 2 Next set from 3: 5 Previous set from 10: 10 - 설명: 인덱스 순서에 따라 낮은 인덱스에서 높은 인덱스로(또는 그 반대로) 탐색.
4. 인덱스 순서와 바이트 순서의 관계
- 내부 표현:
BitSet은long[]배열로 비트를 저장하며, 각long은 64비트를 표현합니다.- 첫 번째
long은 인덱스 063, 두 번째는 64127을 다룹니다. - 바이트로 변환 시, 낮은 인덱스가 낮은 바이트에 매핑되며, 바이트 내에서는 LSB에서 MSB로 진행.
- 첫 번째
- 예: 인덱스 0~15 설정 시:
BitSet: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} Byte array: FF FF (11111111 11111111)
5. 주의점
- 음수 인덱스:
BitSet은 음수 인덱스를 허용하지 않으며,IllegalArgumentException발생. - 바이트 순서:
toByteArray()결과는 little-endian 스타일로, 인덱스 0이 첫 바이트의 LSB에 매핑. - 논리적 vs 물리적 크기:
length()는 마지막true비트의 인덱스 + 1,size()는 할당된 비트 수(64의 배수).
결론
BitSet의 인덱스 순서는 0부터 시작하며, 낮은 인덱스가 LSB, 높은 인덱스가 MSB에 해당합니다. 메서드는 이 순서를 기반으로 비트를 설정, 확인, 연산하며, 바이트 변환 시에도 인덱스 순서가 반영됩니다. 위 예시를 통해 인덱스 순서가 데이터 표현과 탐색에 어떻게 적용되는지 확인할 수 있습니다. 비트 단위 작업 시 이 순서를 이해하면 BitSet을 더 효과적으로 활용할 수 있습니다!