System.Collections.BitArrayクラスはビット列を操作するためのクラスで、ビット列の個々のビット(桁)をbool/Boolean型で扱うことができます。 BitArrayクラスは、bool型のコレクションの形でビット列を表現するクラスとも言えます。 BitArrayクラスには、ビット単位の操作のほか、AND演算・OR演算などのビット演算を提供するメソッドが用意されています。 BitArrayクラスは非ジェネリック型ですが、扱う型がbool型に限定されるため、foreachによる列挙(IEnumerableインターフェイス)以外の操作はタイプセーフとなっているコレクションクラスです。

§1 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();
  }
}
実行結果
32 bits: 11111111000000000011001101010101
64 bits: 0101010100110011111111110000000011111111111111110000000000000000
4 bits: 1010
32 bits: 11111111111111111111111111111111
64 bits: 0000000000000000000000000000000000000000000000000000000000000000


§2 ビットの取得・設定

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();
  }
}
実行結果
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();
  }
}
実行結果
True
False
11111111

§3 ビット演算

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();
  }
}
実行結果
24 bits: 000000001111111100000000
24 bits: 111111110000000011111111
24 bits: 101010100101010110101010

BitArrayクラスでは、ビットシフトやビット列の回転(rotate)に相当する演算は用意されていません。 そのため、そのような操作を行う場合は独自に実装する必要があります。

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();
  }
}
実行結果
8 bits: 00110011
8 bits: 10011001

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

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")));
  }
}
実行結果
ints: 01234567 89ABCDEF
bytes: 67 45 23 01 EF CD AB 89
bools: 1110011010100010110001001000000011110111101100111101010110010001

§5 任意の桁数のビット列

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桁のビット列
  }
}

§6 BitArrayとその他のクラス

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

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

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