読者です 読者をやめる 読者になる 読者になる

Javaでビットベクトル(6)

Java

Javaでビットベクトルを作れるようになりました。

今回は、構築したビットベクトルをデバッグしやすいように表示するメソッドを作り、Mainメソッドから実際に実行してみようと思います。

まず、構築したビットベクトルをすべて表示するクラスを作ろうと思います。

BitVectorクラスにprint()メソッドを作成しましょう。

public void print() {
	for ( long i = 0; i < this.mBitSize; i++ ) {
		int wordPos = (int) ( i / WORD_BIT_SIZE );
		int pos = (int) (i % WORD_BIT_SIZE);
		if ( 0 == pos ) {
			
			if ( 0 != i ) {
				System.out.println();
			}
			
			System.out.printf( "%5d-%5d",
					i,
					((wordPos + 1 ) * WORD_BIT_SIZE) - 1
					);
			System.out.print( ":" );
		}
		
		int word = this.mBitVector[wordPos];
		word = 1 & ( word >> pos );
		
		if ( 1 == word ) {
			System.out.print( 1 );
		} else {
			System.out.print( 0 );
		}
		
	}
	
	System.out.println();
}

1が立っていたら1を出力して、0だったら0を出力するシンプルなメソッドです。
サイズが64のビットベクトルの場合、実行すると、以下のようになります。

    0-   31:00000000001000000000000000000000
   32-   63:00000000000000000000000000000000

簡単ですね。

最後に完成したMainクラスとビットベクトルクラスを掲載します。

まず、Mainクラスです。

public class BitVectorMain {

	public static void main(String[] args) {
		
		// ビットベクトルを生成する。
		BitVector bv = new BitVector( 1000 );
		
		// 指定した位置にビットを設定する。
		for ( long i = 0; i < bv.getBitSize(); i++ ) {
			if ( 0 == ( i % 5 ) ) {
				bv.setValue(i, 1);
			}
		}
		
		// 構築したビットベクトルを表示する。
		bv.print();
		
	}

}

次に、ビットベクトルクラスです。

public class BitVector {
	
	// 1バイトのビット数
	static final long BYTE_BIT_SIZE = 8;
	
	// WORDのバイト数
	static final long WORD_BYTE_SIZE = 4;
	// WORDのビット数
	static final long WORD_BIT_SIZE;
	static {
		WORD_BIT_SIZE = WORD_BYTE_SIZE * BYTE_BIT_SIZE;
	}

	// ビットベクトル
	private int mBitVector[];
	// WORD数
	private long mWordSize;
	// ビットベクトルのビット数
	private long mBitSize;
	
	public BitVector( final long pMaxBitSize ) {
		
		if ( 0 >= pMaxBitSize ) {
			// エラーとする。
			throw new MyProjectRuntimeException(new IllegalArgumentException());
		}
		
		long wordSize = (pMaxBitSize / WORD_BIT_SIZE) + 1;
		
		// intの範囲に収まっているか確認する。
		if ( ((long) Integer.MAX_VALUE) <= wordSize ) {
			throw new MyProjectRuntimeException(new IllegalArgumentException());
		}
		
		// 指定されたビットベクトルのサイズを保持する。
		this.mBitVector = new int [ (int) wordSize ];
		this.mWordSize = wordSize;
		this.mBitSize = ( wordSize ) * WORD_BIT_SIZE;
	}
	
	public long getWordSize() {
		return this.mWordSize;
	}
	
	public long getBitSize() {
		return this.mBitSize;
	}
	
	/**
	 * 
	 * <p>
	 * 指定した場所に指定した値のビットを立てる。
	 * </p>
	 * 
	 * @param pTargetPos ビtットを立てたい位置
	 * @param pValue ビット
	 */
	public void setValue( final long pTargetPos, final int pValue ) {
		
		// pPosの値のチェック
		if ( 0 > pTargetPos && this.mBitSize <= pTargetPos ) {
			// 負の長さであってはならない。
			// 格納できるビット数を超えてはならない。
			throw new MyProjectRuntimeException(new IllegalArgumentException());
		}
		
		// pValueの値のチェック
		if ( 0 != pValue && 1 != pValue ) {
			throw new MyProjectRuntimeException(new IllegalArgumentException());
		}
		
		// ビットを立てたいWORDがいったいどこにあるのか?
		int wordPos = (int) (pTargetPos / WORD_BIT_SIZE);
		
		// WORD内のどこなのか?
		int shift = (int) (pTargetPos % WORD_BIT_SIZE);
		
		int word = this.mBitVector[ wordPos ];
		if ( 1 == pValue ) {
			// 1の場合
			word = word | ( 1 << shift );
		} else {
			// 0の場合
			word = word & ~( 1 << shift );
		}
		
		this.mBitVector[ wordPos ] = word;
		
	}
	
	/**
	 * 
	 * <p>
	 * ビットベクトルを表示する。
	 * </p>
	 * 
	 */
	public void print() {
		for ( long i = 0; i < this.mBitSize; i++ ) {
			int wordPos = (int) ( i / WORD_BIT_SIZE );
			int pos = (int) (i % WORD_BIT_SIZE);
			if ( 0 == pos ) {
				
				if ( 0 != i ) {
					System.out.println();
				}
				
				System.out.printf( "%5d-%5d",
						i,
						((wordPos + 1 ) * WORD_BIT_SIZE) - 1
						);
				System.out.print( ":" );
			}
			
			int word = this.mBitVector[wordPos];
			word = 1 & ( word >> pos );
			
			if ( 1 == word ) {
				System.out.print( 1 );
			} else {
				System.out.print( 0 );
			}
			
		}
		
		System.out.println();
	}

}

このクラスを実行すると、以下のようになります。

    0-   31:10000100001000010000100001000010
   32-   63:00010000100001000010000100001000
   64-   95:01000010000100001000010000100001
   96-  127:00001000010000100001000010000100
  128-  159:00100001000010000100001000010000
  160-  191:10000100001000010000100001000010
  192-  223:00010000100001000010000100001000
  224-  255:01000010000100001000010000100001
  256-  287:00001000010000100001000010000100
  288-  319:00100001000010000100001000010000
  320-  351:10000100001000010000100001000010
  352-  383:00010000100001000010000100001000
  384-  415:01000010000100001000010000100001
  416-  447:00001000010000100001000010000100
  448-  479:00100001000010000100001000010000
  480-  511:10000100001000010000100001000010
  512-  543:00010000100001000010000100001000
  544-  575:01000010000100001000010000100001
  576-  607:00001000010000100001000010000100
  608-  639:00100001000010000100001000010000
  640-  671:10000100001000010000100001000010
  672-  703:00010000100001000010000100001000
  704-  735:01000010000100001000010000100001
  736-  767:00001000010000100001000010000100
  768-  799:00100001000010000100001000010000
  800-  831:10000100001000010000100001000010
  832-  863:00010000100001000010000100001000
  864-  895:01000010000100001000010000100001
  896-  927:00001000010000100001000010000100
  928-  959:00100001000010000100001000010000
  960-  991:10000100001000010000100001000010
  992- 1023:00010000100001000010000100001000

ここまで、ビットベクトルを作ってきましたが、ビットを設定したりするような操作だけが必要であれば、Javaには標準のクラスが存在します。
BitSetというクラスです。以下にJavadocを示します。

http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/BitSet.html

いずれは性能評価してみたいところですが、標準のクラスなので、性能面も十分考慮されているでしょう。
標準機能で足りる場合は、標準を使うのが定石です。

わざわざJavaでビットベクトルのクラスを自分で実装したのは、簡潔ビットベクトルをJavaで実装するためです。

次回は、タイトルを変更して、「Javaで簡潔ビットベクトル」シリーズを書いていこうと思います。