.NET中的位操作:BitVector32结构
BitVector32结构体位于System.Collections.Specialized命名空间内,相对.NET中另外一个位容器BitArray,他的优点是速度快,占用空间小,并可以存储小数字。他内部用一个32位的整数来存储数据,因此只能存储32位的比特数据。
温习位操作
在看BitVector32前,温习一下简单的位操作还是很有必要的,常见的位操作无非就是与(AND),或(OR),非(NOT)。一般情况下,将一位设置成1就是把这个为或1的操作,将一位设置成0就是把这个位与0的操作。同时或0,与1结果不变。
这样的话,比如一个8位的数据:
0000 1111 |
我们想把第二个0设置成1,那么把他和下面这个数进行或操作
0100 0000 |
结果就是
0100 1111 |
第二位变成了1
还是这个数
0000 1111 |
如果想把最后一个1设置成0,那么吧他和下面这个数进行与操作
1111 1110 |
结果就是
0000 1110 |
那么我们可以这样总结一下:
想要操作一个位,我们把其他位设置成0,把这个位设置成1,这个数就是所谓的位掩码(也称位屏蔽,MSDN里用的是位屏蔽)
那么如果想打开一个位(就是把这个位设置成1):源数据 = 源数据 OR 位掩码
如果想关掉一个位(就是把这个位设置成0):源数据 = 源数据 AND 位掩码取反
//位掩码取反就是非(NOT)操作:0变成1,1变成0
BitVector32的位操作
了解了基本位操作BitVector32的理解就会简单多了。
首先BitVector32本质上用一个32位的数来表示数据,那么初始化BitVector32结构时必须制定一个最初值,用户可以传入一个int或者另一个已经存在的BitVector32来构造一个新的BitVector32.
BitVector32的Data属性返回一个int用来表示内部数据,如果用来显示BitVector32的内容,这个Data是没有意义的,因为他是十进制化的结果,这时候用BitVector32的ToString方法就可以返回有用的文字说明。
BitVector32 bits = new BitVector32(0xF); //初始化BitVector32:设置低4位为1 0x 00 00 00 00 00 00 00 0F Console.WriteLine(bits.Data); //(十六进制)0xF 等于 (二进制)1111 等于 (十进制)15 所以输出15 Console.WriteLine(bits.ToString()); //输出:BitVector32{00000000000000000000000000001111} (看得出来:后四位是1) |
接下来就是最重要的位操作了。
BitVector32结构体提供索引器(Indexer)可以直接通过bool对象操作BitVector32结构,索引器参数是int,这个int可不是指第几位的意思(BitArray中的索引器是第几位),而是需要一个位掩码(位屏蔽),通过这个BitVector32通过这个位掩码来操作内部比特位,来看看Reflector下BitVector32索引器的源码
struct BitVector32 { //data 就是 BitVector32的Data属性的对应字段 public bool this[int bit] { get { return ((this.data & bit) == ((ulong)bit)); //通过数据和掩码的与操作,来将其他位清0,保留掩码的设置位 //如果操作后的结果等于掩码,很显然这是操作位就是1,返回true //否则的话,返回false } set { if (value) { this.data |= (uint)bit; //数据 = 数据 OR 掩码,将指定位设置成1 } else { this.data &= (uint)~bit; //数据 = 数据 AND 掩码取反,将指定位设置成0 //C# 取反位操作运算符:~ } } } } |
上面代码我加了注释,可以看到,BitVector32的位操作就是利用普通位掩码的操作。
好了那么用BitVector32索引器操作其实就是定义好位掩码,接着取回信息或者赋值就可以了。
//注意using System.Collections.Specialized; int mask1 = 1; //掩码代表最后一位:二进制表示:0...0001 int mask2 = 4; //掩码代表倒数第三位:二进制表示:0...0000100 BitVector32 bits = new BitVector32(-1); //-1补码:1...1111 //设置BitVector32全部为1 Console.WriteLine(bits); Console.WriteLine("设置最后一位和倒数第三位位0"); bits[mask1] = bits[mask2] = false; Console.WriteLine(bits); Console.WriteLine("设置倒数第三位为1"); bits[mask2] = true; Console.WriteLine(bits); |
输出:
BitVector32{11111111111111111111111111111111} 设置最后一位和倒数第三位位0 BitVector32{11111111111111111111111111111010} 设置倒数第三位为1 BitVector32{11111111111111111111111111111110} |
CreateMask方法
BitVector还有一个CreateMask方法,他的作用就是方便用户定义位掩码。
CreateMask(无参数):返回第一个位(最低位)的位掩码,那么就是0…00001,十进制的话就是1
CreareMask(int):首先判断一直位掩码的合法性,并返回向左移1位的结果。
参考CreateMask的源代码:
public static int CreateMask(int previous) { if (previous == 0) { return 1; } if (previous == -2147483648) //这个数等于Int32.MinValue 二进制是100...0,无法再向左移位,因此异常 { throw new InvalidOperationException(SR.GetString("BitVectorFull")); //抛出异常 } return (previous << 1); //向左移1位 } |
当然如果你掌握了上述位操作和位掩码的概念,我个人感觉CreateMask有点多此一举呵呵,我们就直接用MSDN的例子(我改了改注释)
// 初始化BitVector32:全部0 BitVector32 myBV = new BitVector32(0); // 创建最低位的掩码,然后陆续创建倒数第二位,倒数第三位……倒数第五位的掩码 int myBit1 = BitVector32.CreateMask(); int myBit2 = BitVector32.CreateMask(myBit1); int myBit3 = BitVector32.CreateMask(myBit2); int myBit4 = BitVector32.CreateMask(myBit3); int myBit5 = BitVector32.CreateMask(myBit4); Console.WriteLine("最初值: \t{0}", myBV.ToString()); // 设置倒数第三位为1 myBV[myBit3] = true; Console.WriteLine("myBit3 = TRUE \t{0}", myBV.ToString()); // 将掩码加起来,同时设置两个位 myBV[myBit4 + myBit5] = true; Console.WriteLine("myBit4 + myBit5 = TRUE \t{0}", myBV.ToString()); myBV[myBit1 | myBit2] = true; Console.WriteLine("myBit1 | myBit2 = TRUE \t{0}", myBV.ToString()); |
输出:
最初值: BitVector32{00000000000000000000000000000000} myBit3 = TRUE BitVector32{00000000000000000000000000000100} myBit4 + myBit5 = TRUE BitVector32{00000000000000000000000000011100} myBit1 | myBit2 = TRUE BitVector32{00000000000000000000000000011111} |
如果你手动输出上面例子中的myBit1-5的值,其实就是我们上面讲到的倒数一到五位的掩码值。
使用BitVector32.Section来存储小整数
BitVector32的最后一项功能就是存储小整数,这种情景很不常见,可能会用在存储空间极低的设备上。打个比方就是:比如你存3个数,一个在0-10之内,剩下两个在0-300之内。其实一般情况下,我们用3个int存就可以。如果省空间的话,用一个byte,两个short。如果还想省空间的话,用一个int存就可以,那么这个时侯,就需要BitVector32.
使用BitVector32的静态方法CreateSection返回一个BitVector32.Section结构体。CreateSection需要一个int来指定所存区域的最大整数值(注意只能存0-这个最大值),BitVector32会根据这个最大值来分配所占位长度。接着后续CreateSection函数的调用必须传入之前的Section,因为Section的创建是建立在前面Section没有占用的空闲位。
同时,BitVector32还拥有另一个重载的索引器专门针对Section来进行操作。
代码示例:
//初始化全部0 BitVector32 bits = new BitVector32(0); //创建Section BitVector32.Section sec1 = BitVector32.CreateSection(10); BitVector32.Section sec2 = BitVector32.CreateSection(300, sec1); BitVector32.Section sec3 = BitVector32.CreateSection(300, sec2); //设置每一个Section,注意值要在定义范围之内 bits[sec1] = 9; bits[sec2] = 123; bits[sec3] = 289; Console.WriteLine("Section 1值:{0}", bits[sec1]); Console.WriteLine("Section 2值:{0}", bits[sec2]); Console.WriteLine("Section 3值:{0}", bits[sec3]); //来看看32位的空间用的怎么样 Console.WriteLine(bits); /* * 这句输出:BitVector32{00000000001001000010011110111001} * 前面还有很多0,那么应该还可以存一些数据,果然省空间啊,呵呵 * */ |
输出:
Section 1值:9 Section 2值:123 Section 3值:289 BitVector32{00000000001001000010011110111001} |
版权
作者:Mgen(刘圆圆),转载请注明此出处。
作者博客:http://www.cnblogs.com/mgen