BitSet Class in Java – Compact Sets of Bits
java.util.BitSet is a growable vector of bits, where each bit is indexed by a non-negative integer and is either true (set) or false (clear). It stores 64 flags per long, making it far more compact than a boolean[] (one byte per flag) or a Set<Integer>, and it supports fast bulk logical operations such as AND, OR and XOR.
This tutorial covers the core methods, the difference between cardinality, length and size, set operations, iterating over set bits, and a classic use case: the Sieve of Eratosthenes.
1. Setting, Reading and Iterating Bits
import java.util.BitSet;
public class BitSetBasics {
public static void main(String[] args) {
BitSet bits = new BitSet(); // all bits start as false; grows as needed
bits.set(1);
bits.set(4);
bits.set(6, 9); // sets 6, 7, 8 (end exclusive)
System.out.println("bits : " + bits);
System.out.println("get(4), get(5): " + bits.get(4) + ", " + bits.get(5));
System.out.println("cardinality() : " + bits.cardinality() + " (number of set bits)");
System.out.println("length() : " + bits.length() + " (highest set bit + 1)");
System.out.println("size() : " + bits.size() + " (bits of storage)");
bits.clear(7);
bits.flip(0);
System.out.println("after clear(7), flip(0): " + bits);
System.out.println("nextSetBit(2) : " + bits.nextSetBit(2));
System.out.println("nextClearBit(0): " + bits.nextClearBit(0));
System.out.print("iterate set bits:");
for (int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i + 1)) System.out.print(" " + i);
System.out.println();
System.out.println("stream sum : " + bits.stream().sum());
}
}
bits : {1, 4, 6, 7, 8}
get(4), get(5): true, false
cardinality() : 5 (number of set bits)
length() : 9 (highest set bit + 1)
size() : 64 (bits of storage)
after clear(7), flip(0): {0, 1, 4, 6, 8}
nextSetBit(2) : 4
nextClearBit(0): 2
iterate set bits: 0 1 4 6 8
stream sum : 19
| Method | Purpose |
|---|---|
set(i), set(from, to), set(i, boolean) | Set bits to true (or a given value) |
get(i), get(from, to) | Read a bit, or a range as a new BitSet |
clear(i), clear() | Set bits to false |
flip(i) | Toggle a bit |
cardinality() | Number of set bits |
length() | Index of the highest set bit + 1 (the "logical" size) |
size() | Bits of storage currently allocated (multiple of 64) |
nextSetBit(i), nextClearBit(i), previousSetBit(i) | Navigate efficiently |
isEmpty(), stream() | Any bit set; stream of set indexes |
Ranges use an inclusive start and exclusive end, like substring. Negative indexes throw IndexOutOfBoundsException.
2. Logical Operations
Bitwise operations work on all bits at once, making set algebra very fast. They modify the BitSet they are called on, so clone it first if you need to keep the original:
import java.util.BitSet;
public class BitSetOperations {
static BitSet of(int... indexes) {
BitSet b = new BitSet();
for (int i : indexes) b.set(i);
return b;
}
public static void main(String[] args) {
// Days of the month on which two employees are available
BitSet asha = of(1, 2, 5, 8, 9, 15);
BitSet ravi = of(2, 3, 8, 15, 20);
BitSet both = (BitSet) asha.clone(); both.and(ravi); // intersection
BitSet either = (BitSet) asha.clone(); either.or(ravi); // union
BitSet onlyOne = (BitSet) asha.clone(); onlyOne.xor(ravi); // symmetric difference
BitSet ashaOnly = (BitSet) asha.clone(); ashaOnly.andNot(ravi);
System.out.println("Asha : " + asha);
System.out.println("Ravi : " + ravi);
System.out.println("and (both) : " + both);
System.out.println("or (either) : " + either);
System.out.println("xor : " + onlyOne);
System.out.println("andNot : " + ashaOnly);
System.out.println("intersects : " + asha.intersects(ravi));
}
}
Asha : {1, 2, 5, 8, 9, 15}
Ravi : {2, 3, 8, 15, 20}
and (both) : {2, 8, 15}
or (either) : {1, 2, 3, 5, 8, 9, 15, 20}
xor : {1, 3, 5, 9, 20}
andNot : {1, 5, 9}
intersects : true
| Method | Set operation |
|---|---|
and(other) | Intersection |
or(other) | Union |
xor(other) | Symmetric difference |
andNot(other) | Difference |
intersects(other) | Do they share any set bit? |
3. Use Case: Sieve of Eratosthenes
import java.util.BitSet;
public class SieveOfEratosthenes {
public static void main(String[] args) {
int n = 50;
BitSet composite = new BitSet(n + 1); // 1 bit per number instead of 1 byte (boolean[])
for (int p = 2; (long) p * p <= n; p++) {
if (!composite.get(p)) {
for (int m = p * p; m <= n; m += p) composite.set(m);
}
}
StringBuilder primes = new StringBuilder();
for (int i = composite.nextClearBit(2); i <= n; i = composite.nextClearBit(i + 1)) primes.append(i).append(' ');
System.out.println("Primes up to " + n + ": " + primes.toString().trim());
BitSet flags = BitSet.valueOf(new long[] {0b1011L}); // from a long bit mask
System.out.println("valueOf(0b1011) = " + flags + ", toLongArray()[0] = " + flags.toLongArray()[0]);
}
}
Primes up to 50: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
valueOf(0b1011) = {0, 1, 3}, toLongArray()[0] = 11
BitSet.valueOf and toLongArray/toByteArray convert between a BitSet and raw bit masks, which is useful for storage and network protocols.
4. When to Use BitSet
- Large sets of flags indexed by integers: visited nodes, available seats, feature flags, permissions.
- Fast set operations on dense integer sets.
- Memory-sensitive algorithms such as sieves and Bloom filters.
For sets of enum constants, use EnumSet, which is backed by a bit vector and type-safe. For sparse sets with huge indexes (such as a few IDs around one billion), a HashSet<Integer> may use less memory. BitSet is not thread-safe; synchronize externally if it is shared.
5. Summary
BitSetis a growable, compact vector of bits indexed by non-negative integers.- Use
set,get,clearandflip; iterate withnextSetBitorstream(). cardinalitycounts set bits,lengthis highest set bit + 1,sizeis allocated storage.and,or,xorandandNotperform fast in-place set operations.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What is BitSet in Java?
A class in java.util that stores a growable sequence of bits, each indexed by a non-negative integer.
Q2. What is the default value of bits in a BitSet?
All bits are initially false.
Q3. Does a BitSet grow automatically?
Yes. Setting a bit beyond the current size enlarges the internal array.
Q4. What is the difference between size() and length()?
size() is the allocated storage in bits; length() is the index of the highest set bit plus one.
Q5. What does cardinality() return?
The number of bits set to true.
Q6. How do I iterate over set bits?
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) { ... }, or bs.stream().forEach(...).
Q7. Is BitSet thread-safe?
No. Use external synchronization when multiple threads modify the same BitSet.
Q8. Why use BitSet instead of boolean[]?
It uses one bit per flag instead of one byte, grows automatically and provides fast bulk logical operations.
Q9. Do and() and or() create a new BitSet?
No. They modify the BitSet they are called on. Clone it first to keep the original.
Q10. Can BitSet indexes be negative?
No. Negative indexes throw IndexOutOfBoundsException.
Q11. How do I convert a BitSet to a long?
bs.toLongArray() returns the words; for up to 64 bits use the first element. BitSet.valueOf(long[]) converts back.
Q12. What does flip() do?
It toggles a bit (or range): true becomes false and false becomes true.
Q13. What is the difference between BitSet and EnumSet?
Both are bit vectors. EnumSet is a type-safe Set of enum constants; BitSet works with plain integer indexes.
Q14. Does BitSet implement Set?
No. It is not part of the Collections Framework interfaces, but it implements Cloneable and Serializable.
Q15. How are two BitSets compared?
equals returns true when exactly the same bits are set, regardless of allocated size.
Q16. How much memory does a BitSet use?
About one bit per index up to the highest set bit, stored in 64-bit words, plus a small object overhead.
Q17. Is BitSet good for sparse data?
Not when indexes are very large and few bits are set, because storage grows with the highest index. A hash set is better then.
Q18. What does nextClearBit do?
It returns the index of the first false bit at or after the given index, useful for finding free slots.
Q19. Can BitSet be used for a Bloom filter?
Yes. Bloom filters store hashed positions in a bit array, and BitSet is a common backing structure.
Q20. Since which version is BitSet available?
Java 1.0; stream(), valueOf and toLongArray were added in Java 7–8.