Bitmap
什么是bitmap?
来自于《编程珠玑》。所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。
举个例子。我们要存储3,4,5,6,7这几个元素。如果用int[]来存储的话,我们需要5*32bit。我们知道int32默认值是0,它是由32个0组成的。这样的话我们能不能用它的每个bit位来存储一个数字呢?1-32位代表1-32这32个数字。而0代表不存在、1代表存在。这样的话,我们一个int32就可以存储32个数字了。话不多说,马上开搞。其实原理都非常简单~
static void Main(string[] args) { BitMap map = new BitMap(); map.Add(11); map.Add(22); map.Add(3); map.Add(7); map.Add(6); foreach (var item in map) { Console.WriteLine(item); } } public class BitMap : IEnumerable<int> { private int[] _bucket; public BitMap() { _bucket = new int[10]; } private void Expand(int count) { var temp = new int[count]; _bucket.CopyTo(temp, 0); _bucket = temp; } public void Add(int value) { int index = (value - 1) / 32;//得到在bucket中的下标 if (index + 1 > _bucket.Length)//如果bucket太小,就进行扩充 { Expand(index + 1); } var item = _bucket[index];//获得对应位置中的元素 var binary = Convert.ToString(item, 2).PadLeft(32, '0').Select(x => x.ToString()).ToList();//将item转换成2进制的字符串,再转换为list int bitindex = (value - 1) % 32; binary[bitindex] = "1"; var binarystr = binary.Aggregate((x, y) => x + y); var result = Convert.ToInt32(binarystr, 2); _bucket[index] = result; } public IEnumerator<int> GetEnumerator() { for (int i = 0; i < _bucket.Length; i++) { var item = _bucket[i]; if (item == 0) continue; string binary = Convert.ToString(item, 2).PadLeft(32, '0'); for (int j = 0; j < binary.Length; j++) { if (binary[j] == '1') { yield return i * 32 + j + 1; } } } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new NotImplementedException(); } }
这样我们的bitmap就已经构造完成了。
当然,如果你懂位运算的话,我们还可以改成这样
static void Main(string[] args) { BitMap map = new BitMap(); map.Add(11); map.Add(22); map.Add(3); map.Add(7); map.Add(6); foreach (var item in map) { Console.WriteLine(item); } } public class BitMap : IEnumerable<int> { private int[] _bucket; public BitMap() { _bucket = new int[10]; } private void Expand(int count) { var temp = new int[count]; _bucket.CopyTo(temp, 0); _bucket = temp; } public void Add(int value) { int index = (value - 1) / 32;//得到在bucket中的下标 if (index + 1 > _bucket.Length)//如果bucket太小,就进行扩充 { Expand(index + 1); } var item = _bucket[index];//获得对应位置中的元素 int bitindex = (value - 1) % 32;//计算获得需要将哪个bit位置为1 item = item | (1 << (31 - bitindex));//将对应的位置置为1 _bucket[index] = item; } public IEnumerator<int> GetEnumerator() { for (int i = 0; i < _bucket.Length; i++) { var item = _bucket[i]; if (item == 0) continue; for (int j = 0; j < 32; j++) { var step = 31 - j; var temp = 1 << step; if ((item & temp) == 0) continue; var result = i * 32 + j + 1; yield return result; } } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new NotImplementedException(); } }
其实2组代码的实现效果都是一样的。但位运算是直接操作'位'的。所以性能会高点。
当然,bitmap也有缺点,如果有2个相同的元素,它没办法了。因为bit不是0就是1。