# **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) 기본적인 비트 설정 및 조회** ```java 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()`을 활용한 비트 반전** ```java 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)** ```java 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()` 활용** ```java 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. 비트 설정 및 확인 비트를 설정하고 값을 확인합니다. ```java 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` 연산을 수행합니다. ```java 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` 비트를 찾습니다. ```java 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`을 바이트 배열로 변환하고 역변환합니다. ```java 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`의 크기와 개수를 확인합니다. ```java 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). --- ### 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이 첫 번째 바이트, 8~15가 두 번째 바이트로 변환. - 바이트 내부에서는 비트가 LSB에서 MSB로 배치됩니다 (즉, 인덱스 0이 바이트의 최하위 비트). --- ### 3. **인덱스 순서 예시** #### 예시 1: 비트 설정과 확인 ```java 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: 바이트 배열 변환 ```java 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: 비트 탐색 ```java 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`은 인덱스 0~63, 두 번째는 64~127을 다룹니다. - 바이트로 변환 시, 낮은 인덱스가 낮은 바이트에 매핑되며, 바이트 내에서는 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`을 더 효과적으로 활용할 수 있습니다!