【C# 数据结构】BitMap算法的c#实现

 

1.BitMap的应用

在网络同步中(尤其是帧同步),往往需要找一种数据在序列化之后,体积小,数据信息量可观的数据存储方式。

帧同步为例,一个相当复杂的ACT游戏,采用同步交互量最省的帧同步方式进行网络同步,每关键帧只同步玩家的键位操作数据,此时如果使用BitMap算法进行数据结构的设计,只要1byte就可以存8个键位操作,1int则可以存32个键位操作,每一个关键帧的传输量很小。

在c#语言中,已经设计了BitArray这一数据结构,可直接投入使用。本文旨在详解BitMap算法,所以重新用最基础的byte[]数组以及简单的位运算,来重新实现一遍。

 

2.BitMap算法的原理介绍

首先,我们来看一个byte的结构组成

 

 

 

一个byte的有8位,每一位都可以用来存储0或1的值,默认每一位都是0。

 

我们把每一位的位置信息,作为计数,如0就把第0位的值存为1,1就放在第1位的值存为1,2就放在第2位的值存为1,想要表达的数据超过了8位,就再建立一个byte,继续存放。

 

 

 

 

Magic! 如果现在有10亿的数据,原本按32位int存一个数组,需要 4字节*10亿 = 3.7G左右的空间,如果转换成上述的bitmap算法,从4字节存一个数,变成了1字节存8个数,则只需要3.7G / 32 的空间了。

 

3.具体实现与源码解析

 

按照上述分析,我们主要实现目标其实就是对众多byte组成的这一数据结构,可以进行便利的增删改查。

其中要点可以分为以下三点

1.能够快速的根据存入的数据,定位到其在bitmap中的索引位置。
2.定位到位置后,可以方便的修改其值。(0或1)
3.对于多个byte进行恰当的存储。

首先针对第3点,我们可以很快联系到申明一个byte数组,这样数据连续存储,也方便整体遍历。

再者,针对第1点,也不难想到,既然bitmap本质意义是一个byte数组,那么对于存储其中的数据,我们需要定位到其存储在数组的第几号元素,以及在该元素也就是单个byte中,存储于第几位上。

 

一个byte有8位,首位存0,直接上超级简单的数学算法

对于数据N
在数组中的序号(N) = N / 8 = N >> 3
定位到当前序号的byte后
在byte中的位置(N) = N % 8 = N & 0X07

解决了定位,我们再来看第2点,修改当前位置的值

对于当前byte中,第P位的值
设定一个同为8位byte类型的模板值,由1左移P位得到
byte M = (byte)(1 << P) 
1.如果存入,则需将第P位设为1
	byte = byte | M
2.如果清除,则需将P位设为0
	2.1 将模板值M取反,Z = ~M
	2.2 byte = byte & Z

 

 

 

 

 

 

 

同理,我们还可以扩展出算法,对当前的bitmap是否存储了某个值进行判断

byte isContain = byte & M
如果 isContain 不为0,则存在数据N,反之则不存在

 

 

 

所有源码如下

public class CustomBitMap
{
    public byte[] Bits { get; private set; }

    /// <summary>
    /// 数组中的第几个byte
    /// </summary>
    public byte Index { get; private set; }
    
    /// <summary>
    /// 在单个byte中 第几位
    /// </summary>
    public byte Position { get; private set; }

    public CustomBitMap(int length = 8)
    {
        Bits = new byte[length];
        Index = 0;
        Position = 0;
    }


    /// <summary>
    /// byte[] 中 所在的num位置变为1 其他位置不变
    /// </summary>
    /// <param name="num"></param>
    public void Add(int num)
    {
        //num / 8 得到位于数组的第几个byte
        int arrayIndex = num >> 3;
        // num % 8 得到位于单个byte中 第几位
        int position = num & 0x07;

        byte arg = (byte) (1 << position);
        Bits[arrayIndex] |= arg;
    }


    public void Clear(int num)
    {
        //num / 8 得到位于数组的第几个byte
        int arrayIndex = num >> 3;
        // num % 8 得到位于单个byte中 第几位
        int position = num & 0x07;

        //将1 左移并取反
        byte arg = (byte) ~(1 << position);
        
        //随后&上即清除
        Bits[arrayIndex] &= arg;
    }


    public bool Contains(int num)
    {
        //num / 8 得到位于数组的第几个byte
        int arrayIndex = num >> 3;
        // num % 8 得到位于单个byte中 第几位
        int position = num & 0x07;

        //将1 左移并取反
        byte arg = (byte)(1 << position);
        
        return (Bits[arrayIndex] & arg) != 0;
    }
}

 

 
posted @ 2022-06-16 21:41  小林野夫  阅读(1256)  评论(0编辑  收藏  举报
原文链接:https://www.cnblogs.com/cdaniu/