System.Collections.BitArrayクラスはビット列を操作するためのクラスで、任意長かつ固定長のビット列を扱うことができます。 BitArrayクラスでは、個々のビット(桁)をbool/Boolean型として扱います。

BitArrayクラスは、bool型のコレクションの形でビット列を表現するクラスとも言えます。 BitArrayクラスには、ビット単位の操作のほか、AND演算・OR演算などのビット演算を提供するメソッドが用意されています。

BitArrayクラスは非ジェネリックインターフェスのみを持つコレクションクラスですが、扱う型がbool型に限定されているため、foreachによる列挙(IEnumerableインターフェイス)以外の操作はタイプセーフとなっているコレクションクラスです。

BitArray

BitArrayクラスは可変長のコレクションではなく、固定長のコレクションとして動作します。 つまり、固定長の桁数のビット列を扱います。 他のコレクションとは異なり、Addなどのメソッドによって要素(=ビット列の桁)を追加・削除したりコレクションのサイズ(=ビット列の桁数)を変更したりすることはできません。

BitArrayクラスでは、ビット列となるbyte型、int型、bool型のいずれかの配列をコンストラクタに指定してインスタンスを作成します。 int型・byte型の配列では、配列内の各要素の各ビットがBitArrayインスタンスに設定されます。 bool型配列では、各要素が1ビットを表すものとしてBitArrayインスタンスに設定されます。 このほかに、ビット列の桁数を指定してインスタンスを作成することもできます。

BitArrayクラスをforeach文で列挙すると、ビットの各桁がbool型の値(0=False、1=True)として列挙されます。 下位の桁から列挙されるため、ビット順としてはリトルエンディアンとなります。 Countプロパティを参照すれば、BitArrayの現在の要素数(=桁数)を取得することができます。

BitArrayインスタンスの作成・桁の列挙
using System;
using System.Collections;

class Sample {
  static void Main()
  {
    BitArray bits1 = new BitArray(new byte[] {0xff, 0x00, 0xcc, 0xaa}); // byte配列(8×4ビットのビット列)からBitArrayを生成
    BitArray bits2 = new BitArray(new int[] {0x00ffccaa, 0x0000ffff}); // int配列(32×2ビットのビット列)からBitArrayを生成
    BitArray bits3 = new BitArray(new bool[] {true, false, true, false}); // bool配列(1×4ビットのビット列)からBitArrayを生成
    BitArray bits4 = new BitArray(32, true); // 32ビットのビット列を扱うBitArrayを生成 (すべてのビットを1にする)
    BitArray bits5 = new BitArray(64, false); // 64ビットのビット列を扱うBitArrayを生成 (すべてのビットを0にする)

    Print(bits1);
    Print(bits2);
    Print(bits3);
    Print(bits4);
    Print(bits5);
  }

  static void Print(BitArray bits)
  {
    Console.Write("{0} bits: ", bits.Count);

    // foreach文によってBitArrayの各桁の内容を表示する
    foreach (bool bit in bits) {
      Console.Write(bit ? "1" : "0");
    }

    Console.WriteLine();
  }
}
BitArrayインスタンスの作成・桁の列挙
Imports System
Imports System.Collections

Class Sample
  Shared Sub Main()
    Dim bits1 As New BitArray(New Byte() {&hFF, &h00, &hCC, &hAA}) ' Byte配列(8×4ビットのビット列)からBitArrayを生成
    Dim bits2 As New BitArray(New Integer() {&h00FFCCAA, &h0000FFFF}) ' Integer配列(32×2ビットのビット列)からBitArrayを生成
    Dim bits3 As New BitArray(New Boolean() {True, False, True, False}) ' Boolean配列(1×4ビットのビット列)からBitArrayを生成
    Dim bits4 As New BitArray(32, True) ' 32ビットのビット列を扱うBitArrayを生成 (すべてのビットを1にする)
    Dim bits5 As New BitArray(64, False) ' 64ビットのビット列を扱うBitArrayを生成 (すべてのビットを0にする)

    Print(bits1)
    Print(bits2)
    Print(bits3)
    Print(bits4)
    Print(bits5)
  End Sub

  Shared Sub Print(ByVal bits As BitArray)
    Console.Write("{0} bits: ", bits.Count)

    ' foreach文によってBitArrayの各桁の内容を表示する
    For Each bit As Boolean In bits
      Console.Write(If(bit, "1", "0"))
    Next

    Console.WriteLine()
  End Sub
End Class
実行結果
32 bits: 11111111000000000011001101010101
64 bits: 0101010100110011111111110000000011111111111111110000000000000000
4 bits: 1010
32 bits: 11111111111111111111111111111111
64 bits: 0000000000000000000000000000000000000000000000000000000000000000

ビットの取得・設定

BitArrayクラスでは、他のコレクションと同様にインデクサによってインデックス(=桁)を指定してビット値を取得・設定することができます。

インデックスを指定してBitArrayの各ビットの取得・設定を行う
using System;
using System.Collections;

class Sample {
  static void Main()
  {
    // すべてのビットがfalse(0)でサイズが8ビットのBitArrayを作成
    BitArray bits = new BitArray(8, false);

    bits[0] = true; // 0ビット目をtrue(1)にする
    bits[2] = true; // 2ビット目をtrue(1)にする

    // for文によってすべてのビットを表示する (リトルエンディアン)
    for (int i = 0; i < bits.Count; i++) {
      Console.Write(bits[i] ? 1 : 0);
    }
    Console.WriteLine();

    // for文によってすべてのビットを表示する (ビッグエンディアン)
    for (int i = bits.Count - 1; 0 <= i; i--) {
      Console.Write(bits[i] ? 1 : 0);
    }
    Console.WriteLine();
  }
}
インデックスを指定してBitArrayの各ビットの取得・設定を行う
Imports System
Imports System.Collections

Class Sample
  Shared Sub Main()
    ' すべてのビットがfalse(0)でサイズが8ビットのBitArrayを作成
    Dim bits As New BitArray(8, False)

    bits(0) = True ' 0ビット目をTrue(1)にする
    bits(2) = True ' 2ビット目をTrue(1)にする

    ' for文によってすべてのビットを表示する (リトルエンディアン)
    For i As Integer = 0 To bits.Count - 1
      Console.Write(If(bits(i), 1, 0))
    Next
    Console.WriteLine()

    ' for文によってすべてのビットを表示する (ビッグエンディアン)
    For i As Integer = bits.Count - 1 To 0 Step -1
      Console.Write(If(bits(i), 1, 0))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
10100000
00000101

インデックスを指定した取得・設定以外にも、GetメソッドSetメソッドによってもビット値を取得・設定することができます。 また、SetAllメソッドを使えば、BitArray内のすべてのビット値を同じ値にすることもできます。

Get・Set・SetAllメソッドを使ってBitArrayのビット値の取得・設定する
using System;
using System.Collections;

class Sample {
  static void Main()
  {
    // すべてのビットがfalse(0)でサイズが8ビットのBitArrayを作成
    BitArray bits = new BitArray(8, false);

    bits.Set(0, true); // 0ビット目をtrue(1)にする
    bits.Set(2, true); // 2ビット目をtrue(1)にする

    Console.WriteLine(bits.Get(0)); // 0ビット目のビット値を取得する
    Console.WriteLine(bits.Get(1)); // 1ビット目のビット値を取得する

    bits.SetAll(true); // すべてのビットをtrue(1)にする

    foreach (bool bit in bits) {
      Console.Write(bit ? 1 : 0);
    }
    Console.WriteLine();
  }
}
Get・Set・SetAllメソッドを使ってBitArrayのビット値の取得・設定する
Imports System
Imports System.Collections

Class Sample
  Shared Sub Main()
    ' すべてのビットがfalse(0)でサイズが8ビットのBitArrayを作成
    Dim bits As New BitArray(8, False)

    bits.Set(0, True) ' 0ビット目をTrue(1)にする
    bits.Set(2, True) ' 2ビット目をTrue(1)にする

    Console.WriteLine(bits.Get(0)) ' 0ビット目のビット値を取得する
    Console.WriteLine(bits.Get(1)) ' 1ビット目のビット値を取得する

    bits.SetAll(True) ' すべてのビットをTrue(1)にする

    For Each bit As Boolean in bits
      Console.Write(If(bit, 1, 0))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
True
False
11111111

ビット演算

BitArrayクラスでは、各種ビット演算に対応する以下のメソッドが用意されています。 引数に他のビット列を表すBitArrayを指定することで、そのBitArrayとの演算結果を求めることができます。

BitArrayクラスのメソッド
メソッド 動作
Notメソッド すべての桁のビットを反転する
Orメソッド 指定されたBitArrayとの論理和を求める
Andメソッド 指定されたBitArrayとの論理積を求める
Xorメソッド 指定されたBitArrayとの排他的論理和を求める

上記のメソッドによる演算では、メソッドを呼び出したインスタンス自身に演算結果が反映され、さらにインスタンス自身を戻り値として返します。 以下は、上記メソッドを使ってビット演算を行う例です。

BitArrayクラスのメソッドを使ってBitArray同士のビット演算を行う
using System;
using System.Collections;

class sample {
  static void Main()
  {
    // 000000001111111100000000のビット列を持つ24ビットのBitArrayを作成
    BitArray bits = new BitArray(new byte[] {0x00, 0xff, 0x00});

    Print(bits);

    // 全ビットを反転
    Print(bits.Not());

    // 排他的論理和を求める
    Print(bits.Xor(new BitArray(new byte[] {0xaa, 0xaa, 0xaa})));
  }

  static void Print(BitArray bits)
  {
    Console.Write("{0} bits: ", bits.Count);

    foreach (bool bit in bits) {
      Console.Write(bit ? 1 : 0);
    }
    Console.WriteLine();
  }
}
BitArrayクラスのメソッドを使ってBitArray同士のビット演算を行う
Imports System
Imports System.Collections

Class Sample
  Shared Sub Main()
    ' 000000001111111100000000のビット列を持つ24ビットのBitArrayを作成
    Dim bits As New BitArray(New Byte() {&h00, &hFF, &h00})

    Print(bits)

    ' 全ビットを反転
    Print(bits.Not())

    ' 排他的論理和を求める
    Print(bits.Xor(New BitArray(New Byte() {&hAA, &hAA, &hAA})))
  End Sub

  Shared Sub Print(ByVal bits As BitArray)
    Console.Write("{0} bits: ", bits.Count)

    For Each bit As Boolean In bits
      Console.Write(If(bit, 1, 0))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
24 bits: 000000001111111100000000
24 bits: 111111110000000011111111
24 bits: 101010100101010110101010

シフト演算

BitArrayクラスでは、ビットシフトを行うメソッドとして、以下の2つのメソッドが用意されています。 ただし、このメソッドは.NET Standard 2.1/.NET Core 2.0以降でのみ使用できます。

BitArrayクラスのメソッド
メソッド 動作
LeftShiftメソッド 左シフト演算を行う
RightShiftメソッド 右シフト演算を行う

いずれのメソッドも、シフト量を引数で指定することができます。 このメソッドでは、シフトによって欠ける桁には常に0が補われる論理シフトとして動作します。 符号ビットは存在しないものとされ、考慮されません。

BitArrayクラスを使ってビット列のシフトを行う
using System;
using System.Collections;

class sample {
  static void Main()
  {
    // 11001100のビット列を持つ8ビットのBitArrayを作成
    var bits = new BitArray(new byte[] {0xcc});

    Print(bits);

    // ビット列を左方向に1ビット分シフトする
    bits.LeftShift(1);

    Print(bits);

    // ビット列を右方向に2ビット分シフトする
    bits.RightShift(2);

    Print(bits);
  }

  static void Print(BitArray bits)
  {
    Console.Write("{0} bits: ", bits.Count);

    foreach (bool bit in bits) {
      Console.Write(bit ? 1 : 0);
    }
    Console.WriteLine();
  }
}
BitArrayクラスを使ってビット列のシフトを行う
Imports System
Imports System.Collections

Class Sample
  Shared Sub Main()
    ' 11001100のビット列を持つ8ビットのBitArrayを作成
    Dim bits As New BitArray(New Byte() {&hCC})

    Print(bits)

    ' ビット列を左方向に1ビット分シフトする
    bits.LeftShift(1)

    Print(bits)

    ' ビット列を右方向に2ビット分シフトする
    bits.RightShift(2)

    Print(bits)
  End Sub

  Shared Sub Print(ByVal bits As BitArray)
    Console.Write("{0} bits: ", bits.Count)

    For Each bit As Boolean In bits
      Console.Write(If(bit, 1, 0))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
8 bits: 00110011
8 bits: 00011001
8 bits: 01100100

要素の列挙順の都合により、シフト方向と実際にシフトされるビットの方向が逆になっています。


なお、回転シフト・循環シフトに相当する演算は用意されていません。 そのため、そのような操作を行う場合は独自に実装する必要があります。

BitArrayクラスを使ってビット列の右回転を行う
using System;
using System.Collections;

class sample {
  static void Main()
  {
    // 11001100のビット列を持つ8ビットのBitArrayを作成
    BitArray bits = new BitArray(new byte[] {0xcc});

    Print(bits);

    // ビット列を右回転する
    BitArray bits2 = new BitArray(bits.Count);

    for (int i = 0; i < bits.Count; i++) {
      bits2[(i + 1) % bits.Count] = bits[i];
    }

    Print(bits2);
  }

  static void Print(BitArray bits)
  {
    Console.Write("{0} bits: ", bits.Count);

    foreach (bool bit in bits) {
      Console.Write(bit ? 1 : 0);
    }
    Console.WriteLine();
  }
}
BitArrayクラスを使ってビット列の右回転を行う
Imports System
Imports System.Collections

Class Sample
  Shared Sub Main()
    ' 11001100のビット列を持つ8ビットのBitArrayを作成
    Dim bits As New BitArray(New Byte() {&hCC})

    Print(bits)

    ' ビット列を右回転する
    Dim bits2 As New BitArray(bits.Count)

    For i As Integer = 0 To bits.Count - 1
      bits2((i + 1) Mod bits.Count) = bits(i)
    Next

    Print(bits2)
  End Sub

  Shared Sub Print(ByVal bits As BitArray)
    Console.Write("{0} bits: ", bits.Count)

    For Each bit As Boolean In bits
      Console.Write(If(bit, 1, 0))
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
8 bits: 00110011
8 bits: 10011001

その他のビット演算

先行する/後続する0のビット数 (LZCNT/TZCNT)

BitOperationsクラスでは、先行する・後続する0のビットの数を計上するLeadingZeroCountメソッド・TrailingZeroCountメソッドを使用することができます。

これと同様の演算を行うメソッドはBitArrayクラスには用意されていませんが、次のようにして実装することができます。

BitArrayクラスを使って先行/後続する0のビットの数(LZCNT/TZCNT)を計上する 
using System;
using System.Collections;
using System.Linq;

static class Sample {
  static void Main()
  {
    var bitArray = new BitArray(new byte[] {0b_0000_0000, 0b_0000_0001, 0b_1000_0000, 0b_0000_0000});

    int lzcnt = bitArray.Cast<bool>().TakeWhile(bit => bit == false).Count();
    int tzcnt = bitArray.Cast<bool>().Reverse().TakeWhile(bit => bit == false).Count();

    Console.WriteLine(lzcnt);
    Console.WriteLine(tzcnt);
  }
}
BitArrayクラスを使って先行/後続する0のビットの数(LZCNT/TZCNT)を計上する 
Imports System
Imports System.Collections
Imports System.Linq

Module Sample
  Sub Main()
    Dim bitArray As New BitArray(New Byte() {&B_0000_0000, &B_0000_0001, &B_1000_0000, &B_0000_0000})

    Dim lzcnt As Integer = bitArray.Cast(Of Boolean)().TakeWhile(Function(bit As Boolean)
      Return bit = False
    End Function).Count()
    Dim tzcnt As Integer = bitArray.Cast(Of Boolean)().Reverse().TakeWhile(Function(bit As Boolean)
      Return bit = False
    End Function).Count()

    Console.WriteLine(lzcnt)
    Console.WriteLine(tzcnt)
  End Sub
End Module

立っているビットの数 (POPCNT)

BitOperationsクラスでは、立っているビットの数(1のビットの数)を計上するPopCountメソッドを使用することができます。

これと同様の演算を行うメソッドはBitArrayクラスには用意されていませんが、次のようにして実装することができます。

BitArrayクラスを使って立っているビットの数(POPCNT)を計上する 
using System;
using System.Collections;
using System.Linq;

static class Sample {
  static void Main()
  {
    var bitArray = new BitArray(new byte[] {0b_1111_0000, 0b_1010_0000, 0b_1000_0100, 0b_0010_0001});

    int popcnt = bitArray.Cast<bool>().Count(bit => bit == true);

    Console.WriteLine(popcnt);
  }
}
BitArrayクラスを使って立っているビットの数(POPCNT)を計上する 
Imports System
Imports System.Collections
Imports System.Linq

Module Sample
  Sub Main()
    Dim bitArray As New BitArray(New Byte() {&B_1111_0000, &B_1010_0000, &B_1000_0100, &B_0010_0001})

    Dim popcnt As Integer = bitArray.Cast(Of Boolean)().Count(Function(bit As Boolean)
      Return bit = True
    End Function)

    Console.WriteLine(popcnt)
  End Sub
End Module

ビット列の配列へのコピー

CopyToメソッドを使うと、BitArrayのビット列を配列にコピーすることができます。 このメソッドはコンストラクタと逆の動作をします。 つまり、int[]byte[]をコピー先とした場合は、ビット列の内容がそのままint・byteのビット列としてコピーされます。 bool[]の場合は、各ビットの値が一つのbool値として配列にコピーされます。 これ以外の配列をコピー先とすることはできず、そういった配列を指定した場合は例外ArgumentExceptionがスローされます。

コピー先の配列のほかに、CopyToメソッドではコピー先のインデックスを指定します。 0を指定すれば、配列の先頭にコピーします。

BitArrayクラスのビット列を配列にコピーする
using System;
using System.Collections;
using System.Linq;

class sample {
  static void Main()
  {
    BitArray bits = new BitArray(new int[] {0x01234567, unchecked((int)0x89abcdef)}); // 32×2=64ビットのビット列

    // int型配列にコピーする
    int[] ints = new int[2]; // 32×2ビット分

    bits.CopyTo(ints, 0);

    // 配列の各要素を8桁の16進数として表示
    Console.WriteLine("ints: {0}", string.Join(" ", ints.Select(i => i.ToString("X8"))));

    // byte型配列にコピーする
    byte[] bytes = new byte[8]; // 8×8ビット分

    bits.CopyTo(bytes, 0);

    // 配列の各要素を2桁の16進数として表示
    Console.WriteLine("bytes: {0}", string.Join(" ", bytes.Select(by => by.ToString("X2"))));

    // bool型配列にコピーする
    bool[] bools = new bool[64]; // 1×64ビット分

    bits.CopyTo(bools, 0);

    // 配列の各要素を0または1で表示
    Console.WriteLine("bools: {0}", string.Concat(bools.Select(b => b ? "1" : "0")));
  }
}
BitArrayクラスのビット列を配列にコピーする
Imports System
Imports System.Collections
Imports System.Linq

Class Sample
  Shared Sub Main()
    Dim bits As New BitArray(New Integer() {&H01234567, &H89ABCDEF}) ' 32×2=64ビットのビット列

    ' Integer型配列にコピーする
    Dim ints(1) As Integer ' 32×2ビット分

    bits.CopyTo(ints, 0)

    ' 配列の各要素を8桁の16進数として表示
    Console.WriteLine("ints: {0}", String.Join(" ", ints.Select(Function(i) i.ToString("X8"))))

    ' Byte型配列にコピーする
    Dim bytes(7) As Byte ' 8×8ビット分

    bits.CopyTo(bytes, 0)

    ' 配列の各要素を2桁の16進数として表示
    Console.WriteLine("bytes: {0}", String.Join(" ", bytes.Select(Function(by) by.ToString("X2"))))

    ' Boolean型配列にコピーする
    Dim bools(63) As Boolean ' 1×64ビット分

    bits.CopyTo(bools, 0)

    ' 配列の各要素を0または1で表示
    Console.WriteLine("bools: {0}", String.Concat(bools.Select(Function(b) If(b, "1", "0"))))
  End Sub
End Class
実行結果
ints: 01234567 89ABCDEF
bytes: 67 45 23 01 EF CD AB 89
bools: 1110011010100010110001001000000011110111101100111101010110010001

任意の桁数のビット列

BitArrayクラスでは、桁数を指定してビット列を作成できます。 このとき、桁数として2n以外や奇数を指定することもできます。 また、桁数に0を指定することもできます。 0を指定した場合でもArgumentOutOfRangeExceptionなどの例外はスローされません。

2のn乗や奇数の桁数を持つBitArrayクラスを作成する
using System;
using System.Collections;

class sample {
  static void Main()
  {
    BitArray bits3  = new BitArray(3);  // 3桁のビット列
    BitArray bits7  = new BitArray(7);  // 7桁のビット列
    BitArray bits24 = new BitArray(24); // 24桁のビット列

    BitArray bits0  = new BitArray(0);  // 0桁のビット列
  }
}
2のn乗や奇数の桁数を持つBitArrayクラスを作成する
Imports System
Imports System.Collections

Class Sample
  Shared Sub Main()
    Dim bits3 As New BitArray(3)    ' 3桁のビット列
    Dim bits7 As New BitArray(7)    ' 7桁のビット列
    Dim bits24 As New BitArray(24)  ' 24桁のビット列

    Dim bits0 As New BitArray(0)    ' 0桁のビット列
  End Sub
End Class

BitArrayとその他のクラス

BitArrayクラスでは、内部的には32ビット整数によって各ビットが管理されるようですが、直接整数型を使ってビット演算を行う場合に比べると若干のオーバーヘッドがあります。 ただ、例えば64ビット(long)を超えるような長大な桁数のビット列や、2のべき乗でない桁数のビット演算を行いたい場合には多少の利点があります。

長大な桁数の数を扱いたい場合はBigInteger構造体を使うこともできます。 ただし、この構造体ではANDやORなどのビット演算を行うことはできません。 BigInteger構造体については多倍長整数型を参照してください。

数値や数値型配列のビット表現・バイト列を取得したい場合は、BufferクラスやBitConverterクラスを用いることができます。 詳しくはバイト列操作を参照してください。