BitArray是C# System.Collections内置的集合,用于帮助进行位运算。

BitArray的使用示例

// 创建两个大小为 8 的点阵列
BitArray ba1 = new BitArray(8);
BitArray ba2 = new BitArray(8);
byte[] a = { 60 };
byte[] b = { 13 }; // 把值 60 和 13 存储到点阵列中
ba1 = new BitArray(a);
ba2 = new BitArray(b); // ba1 的内容
Console.WriteLine("Bit array ba1: 60");
for (int i = 0; i < ba1.Count; i++)
{
Console.Write("{0, -6} ", ba1[i]);
}
Console.WriteLine(); // ba2 的内容
Console.WriteLine("Bit array ba2: 13");
for (int i = 0; i < ba2.Count; i++)
{
Console.Write("{0, -6} ", ba2[i]);
}
Console.WriteLine(); BitArray ba3 = new BitArray(8);
ba3 = ba1.And(ba2); // ba3 的内容
Console.WriteLine("Bit array ba3 after AND operation: 12");
for (int i = 0; i < ba3.Count; i++)
{
Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine(); ba3 = ba1.Or(ba2);
// ba3 的内容
Console.WriteLine("Bit array ba3 after OR operation: 61");
for (int i = 0; i < ba3.Count; i++)
{
Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine(); Console.ReadKey();

可以看到只要BitArray完成构造后就可以和其他BitArray进行位运算了

构造原理

接下来看看BitArray是怎么构造实现的,BitArray看起来是一个bool数组,但它内部实际上是由int数组实现的

我们来看看它的构造函数,根据构造函数我们很快明白BitArray内部的构造机制

public BitArray(int length, bool defaultValue)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
} m_array = new int[GetArrayLength(length, 32)];
m_length = length; int fillValue = defaultValue ? unchecked(((int)0xffffffff)) : 0;
for (int i = 0; i < m_array.Length; i++)
{
m_array[i] = fillValue;
}
}
private static int GetArrayLength(int n, int div)
{
return n > 0 ? (((n - 1) / div) + 1) : 0;//-1是防止算术溢出,+ 1是因为必然有一个int数
}

我们先来看其中一个构造函数,构造函数要求提供BitArray的长度,然后通过GetArrayLength计算这个长度的位向量需要提供多少个int数据进行存储,之后根据计算后的结果,分配m_array这个int[] 数组。之后根据defaultValue这个bool变量为int数组中的int值设置默认值0x00000000或0xffffffff

再看下一个构造函数

public BitArray(byte[] bytes) {
if (bytes == null) {
throw new ArgumentNullException("bytes");
} if (bytes.Length > Int32.MaxValue / BitsPerByte) {
throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", BitsPerByte), "bytes");
} m_array = new int[GetArrayLength(bytes.Length, 32/8)];
m_length = bytes.Length * 32/8; int i = 0;
int j = 0;
while (bytes.Length - j >= 4)
{
m_array[i++] = (bytes[j] & 0xff) |
((bytes[j + 1] & 0xff) << 8) |
((bytes[j + 2] & 0xff) << 16) |
((bytes[j + 3] & 0xff) << 24);
j += 4;
} switch (bytes.Length - j)
{
case 3:
m_array[i] = ((bytes[j + 2] & 0xff) << 16);
goto case 2;
case 2:
m_array[i] |= ((bytes[j + 1] & 0xff) << 8);
goto case 1; case 1:
m_array[i] |= (bytes[j] & 0xff);
break;
} }

这次的构造函数是用byte数组初始化,byte是8位的,可以轻易计算出需要32/8个byte数据才能填充一个int数,相应的如果想要将byte中的数据填充到int中需要有所修改,需要将byte数据&0xff获取8位的二进制数据,再通过移位和或运算一步步将数据填充进int中

构造函数还有好几个,不过本质是一样的。通过构造函数提供的信息获取需要的int数据数量用以存放位向量的数据,然后填充数据

数据获取和设置

再看看BitArray内部的其他实现

BitArray的Get

public bool Get(int index) {
if (index < 0 || index >= Length) {
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
} return (m_array[index / 32] & (1 << (index % 32))) != 0;
}

代码本质就是位运算,通过index/32找到存储的int数据,再通过与上(1 << (index % 32))获取这个位置的二进制数据,然后通过!=0操作返回bool数据

BitArray的Set

public void Set(int index, bool value) {
if (index < 0 || index >= Length) {
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
} if (value) {
m_array[index / 32] |= (1 << (index % 32));
} else {
m_array[index / 32] &= ~(1 << (index % 32));
} }

设置查找数据索引的用的是同样的方法,找到后通过或1设置1,与~1设置0

BitArray长度设置

public int Length {
get {
return m_length;
}
set {
if (value < 0) {
throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
} int newints = GetArrayLength(value, 32);
if (newints > m_array.Length || newints + 256 < m_array.Length) {
int[] newarray = new int[newints];
Array.Copy(m_array, newarray, newints > m_array.Length ? m_array.Length : newints);
m_array = newarray;
} if (value > m_length) {
int last = GetArrayLength(m_length, 32) - 1;
int bits = m_length % 32;
if (bits > 0) {
m_array[last] &= (1 << bits) - 1;
} Array.Clear(m_array, last + 1, newints - last - 1);
} m_length = value;
}
}

BitArray长度不是恒定的,当他改变时内部数组会重新生成,之前数组的数据会根据根据新的数组的长度拷贝一部分,源码中拷贝有两部分,前一部分是当内部数组newarray长度有变动时的拷贝,后一部分是在长度m_length有变动时的拷贝。

不过我觉得如果同时满足newints > m_array.Length和value > m_length,后一部分的拷贝有些多余

位运算

public BitArray And(BitArray value) {
if (value==null)
throw new ArgumentNullException("value");
if (Length != value.Length)
throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
Contract.EndContractBlock(); int ints = GetArrayLength(m_length, BitsPerInt32);
for (int i = 0; i < ints; i++) {
m_array[i] &= value.m_array[i];
}
return this;
}
public BitArray Or(BitArray value) {
if (value==null)
throw new ArgumentNullException("value");
if (Length != value.Length)
throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
Contract.EndContractBlock(); int ints = GetArrayLength(m_length, BitsPerInt32);
for (int i = 0; i < ints; i++) {
m_array[i] |= value.m_array[i];
} return this;
} public BitArray Xor(BitArray value) {
if (value==null)
throw new ArgumentNullException("value");
if (Length != value.Length)
throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
Contract.EndContractBlock(); int ints = GetArrayLength(m_length, BitsPerInt32);
for (int i = 0; i < ints; i++) {
m_array[i] ^= value.m_array[i];
} return this;
} public BitArray Not() {
int ints = GetArrayLength(m_length, BitsPerInt32);
for (int i = 0; i < ints; i++) {
m_array[i] = ~m_array[i];
}
return this;
}

虽说这一部分是BitArray的主要功能,但这一部分代码却十分简单

不过需要注意的是,关于And,Or,Xor运算,参与位运算的2个BitArray数据需要拥有相同的Length

迭代器

BitArray继承了接口ICollection,所以支持迭代集合,BitArray专门实现了BitArrayEnumeratorSimple类来实现GetEnumerator(),每次调用GetEnumerator()都会生成一个BitArrayEnumeratorSimple实例

public IEnumerator GetEnumerator()
{
return new BitArrayEnumeratorSimple(this);
}
private class BitArrayEnumeratorSimple : IEnumerator, ICloneable
{
private BitArray bitarray;
private int index;
private int version;
private bool currentElement; internal BitArrayEnumeratorSimple(BitArray bitarray) {
this.bitarray = bitarray;
this.index = -1;
version = bitarray._version;
} public Object Clone() {
return MemberwiseClone();
} public virtual bool MoveNext() {
if (version != bitarray._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
if (index < (bitarray.Count-1)) {
index++;
currentElement = bitarray.Get(index);
return true;
}
else
index = bitarray.Count; return false;
} public virtual Object Current {
get {
if (index == -1)
throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
if (index >= bitarray.Count)
throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
return currentElement;
}
} public void Reset() {
if (version != bitarray._version) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumFailedVersion));
index = -1;
}
}

上面的代码是一个标准的迭代器接口实现,一开始index =-1;只有当第一次调用MoveNext()时,index =0后正式指向一个元素;Current这个属性返回的是currentElement,在MoveNext()调用前,currentElement是它的默认值;每次调用MoveNext(),index自增1,currentElement也会被更新,当index超过集合长度时,index和currentElement不会因为MoveNext()的调用而更新,并且MoveNext会返回false,迭代器对集合的遍历也正式结束。

为什么foreach时集合不能改变?因为迭代器源于设计模式中的迭代器模式,它本质是访问一个容器对象中各个元素,而又不需暴露该对象的内部细节,所以迭代器迭代时集合理论上不允许被改变的,那迭代器如何保证提醒程序员不应该在使用迭代器迭代时修改内部元素呢?仔细观察MoveNext()代码,发现关于version变量检测的异常抛出,没错正是这个version保证了迭代器模式的原则。在上述代码里我为了保证代码的可读性,删除了关于version的代码,实际上int类型version变量在调用构造函数时初始化为0,每次修改集合时都会自增1,这就保证迭代器可以及时检测到version的变更,判断原集合是否变更。

BitArray源码解析的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  5. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  6. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  7. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  8. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  9. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

随机推荐

  1. asp.net core跨平台开发从入门到实战文摘

    第1章 .NET Core 第2章 dotnet命令 第3章 VS Code安装及介绍 第4章 VS2015开发.NET Core 第5章 ASP.NET Core 第6章 EF Core 第7章 A ...

  2. vue的computed属性

    vue的computed属性要注意的两个地方,1,必须有return,2,使用属性不用括号 <div> <input type="text" v-model=&q ...

  3. boost-实用工具:noncopyable、optional、assign

    1.noncopyable 让一个类从noncopyable继承可以实现禁止对象的复制,使用需要包含头文件"boost/noncopyable.hpp"或"boost/u ...

  4. 查看mysql数据库中的所有用户

    SELECT DISTINCT CONCAT('User: ''',user,'''@''',host,''';') AS query FROM mysql.user; @前面为用户名,后面对应的‘% ...

  5. 预装apk

    一般是在device/rockchip/ LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := LanguageSetLOC ...

  6. 安卓TP驱动开发

    转自 blog.csdn.net/dddxxxx/article/details/54312415

  7. keras model.compile(loss='目标函数 ', optimizer='adam', metrics=['accuracy'])

    深度学习笔记 目标函数的总结与整理   目标函数,或称损失函数,是网络中的性能函数,也是编译一个模型必须的两个参数之一.由于损失函数种类众多,下面以keras官网手册的为例. 在官方keras.io里 ...

  8. Linux学习(2)- 正则表达式基础

    Linux学习(2)- 正则表达式基础 一.基础正则表达式介绍与练习 学习内容 正则表达式特殊符号 [:alnum:]代表英文大小写字母及数字 [:alpha:]代表英文大小写字母 [:blank:] ...

  9. Beta阶段第三篇Scrum冲刺博客-Day2

    1.站立式会议 提供当天站立式会议照片一张 2.每个人的工作 (有work item 的ID),并将其记录在码云项目管理中: 昨天已完成的工作. 张晨晨:熟悉代码 郭琪容:了解复习模块需要的部分知识 ...

  10. c# json转换成dynamic对象,然后在dynamic对象中动态获取指定字符串列表中的值

    using Newtonsoft.Json;using System;using System.Collections.Generic;using System.Linq;using System.T ...