大数据常用技巧之位图法
2015-12-07 21:27
148 查看
大数据常用技巧之位图法
介绍
位图的基本概念是用一个位(bit)来标记某个数据的存放状态,由于采用了位为单位来存放数据,所以节省了大量的空间。举个具体的例子,在Java中一般一个int数字要占用32位,如果能用一位就表示这个数,就可以缩减大量的存储空间。一般把这种方法称为位图法,即Bitmap。BitSet
正因为位图运算在空间方面的优越性,很多语言都有直接对它的支持。如在C++的STL库中就有一个bitset容器。而在Java中,在java.util包下也有一个BitSet类用来实现位图运算。此类实现了一个按需增长的位向量。BitSet的每一位都由一个boolean值来表示。用非负的整数将BitSet的位编入索引,可以对每个编入索引的位进行测试、设置或者清除。通过逻辑与、逻辑或和逻辑异或操作,可以使用一个BitSet修改另一个BitSet的内容。需要注意的是BitSet底层实现是通过一个long数组来保存数据的,也就是说它增长的最小单位是一个long所占的逻辑位,即64位。但如果不是对存储区空间有极致的要求,而且对自己的基本功非常有信心,不建议自己去实现一个跟BitSet类似的类来实现相关的功能。因为jdk中的类都是极精简并做过合理优化的,BitSet类比较长,举其中一个方法来说。
//这是用来在两个BitSet之间做或运算的方法 public void or(BitSet set) { //自己与自己做或运算结果不变 if (this == set) return; int wordsInCommon = Math.min(wordsInUse, set.wordsInUse); //考虑是否需要扩容,因为参数可能比原来的set要长 if (wordsInUse < set.wordsInUse) { ensureCapacity(set.wordsInUse); wordsInUse = set.wordsInUse; } //长的BitSet超出短的的部分结果不变 for (int i = 0; i < wordsInCommon; i++) words[i] |= set.words[i]; if (wordsInCommon < set.wordsInUse) System.arraycopy(set.words, wordsInCommon, words, wordsInCommon, wordsInUse - wordsInCommon); //参数校验,确保所有参数之间关系正确 checkInvariants(); }
典型应用
海量数据排序
从最简单的情况说起,如果要对90个小于100的不重复的正整数排序。用位图的思想就是先申请一块100bit的空间,第一遍遍历所有的数,将出现的数字在位图中对应的位置置为1;第二遍遍历位图,依次输出值为1的位对应的数字。先且不说这种情况出现的频率不是很高,就仅这种情况,还是有很多其他的排序算法有它们自己的优势(不用额外占用空间之类)。但更进一步,如果我们把数字范围放大,对1000,000,000中的900,000,000个不重复的正整数排序,由于需要的内存非常大,其他算法在不分治的情况下就很难再处理这个问题。而用位图法只需要1000000000/(8*1024*104)=119.2MB空间,对现在的计算机来说没有任何问题。海量数据去重
看一个比较常见的面试题:在2.5亿个整数中找出不重复的整数,内存不足以放下算有的数。我们可以采用两位的位图法,为每个数分配两位,00表示没有出现,01表示出现一次,10表示出现多次,11没有意义。这样下来总共需要232∗2=1GB(这里没有限定整数的范围,所有把所有32位整数都考虑进去)的内存。接下去扫描着2.5亿个整数,查看位图中相对应的位,如果是00就变为01,如果是01就变为10,其他情况保持不变。扫描完成后仍为01的整数就是需要查找的数。数据压缩
假设有这样一份数据,记录了全国1990-1999年出生的人的姓名和出生年月的键值对。假设正好有一千万人,那就要存储一千万个姓名和年份。如何运用Bitmap的思想来压缩数据呢。下面提供几种思路。从人的角度来看,由于一共就只有10个年份,可以用4个bit将它们区分开。如0000表示1990年,1001表示1999年。那一个人的出生年份就可以用4个bit位来表示,进而一千万个年份就可以压缩为一千万个4位的bit组;从另一个角度来看这个问题,我们有10个年份,每个人要么是要么不是在这个年份出生。每个人对于年份来说就可以抽象为一个bit位,所以我们可以把一千万的年龄压缩为10个一千万位的bit组。这样压缩的力度不如按人的角度压缩的大,但从年份出发的问题会有一定的优势,如有哪些人是1990年出生的,只需遍历1990年对应的那个bit组就可以了。
可以看出来不管从哪个角度,bitmap的压缩都是建立在数据中存在大量的冗余数据的基础上的,如年份。而在上面的问题中,年份的分布是散乱的,那假如我们事先把数据进行了排序,把相同的出生年份的人排在一起,那数据就可以进一步压缩。这样一来就只要记录每个年份的人数,就可以根据下标来判断每个人的出生年份。
总结
Bitmap适用于数据规模大,但数据状态少的情况。同时Bitmap在存在以下一些不足:存储离散数据利用率低
Bitmap申请空间时要根据最大的数来决定申请的空间大小,如果数据是离散的,那空间的利用率就会非常低。
不适合多状态
一个bit只能表示两种状态,如果要表示更多的状态,就需要更多的状态位来实现。如果一个数字需要多个状态位来表示的话,Bitmap的优越性也会大打折扣,而且复杂度却在增加。
可读性差
将数据抽象为bit不利于理解,尤其是用多个bit位来表示一个数时。
性能一般
需要维护额外的逻辑,计算速度会受到一定的影响。
相关文章推荐
- 康诺云推出三款智能硬件产品,为健康管理业务搭建数据池
- C#实现位图转换成图标的方法
- MySQL中使用innobackupex、xtrabackup进行大数据的备份和还原教程
- C++实现位图排序实例
- C语言实现的bitmap位图代码分享
- 数据结构之位图(bitmap)详解
- php+ajax导入大数据时产生的问题处理
- C# 大数据导出word的假死报错的处理方法
- 用Python实现协同过滤的教程
- Python利用多进程将大量数据放入有限内存的教程
- mongodb常遇到的错误。
- Stack数据结构的特点后进先出的应用:大数据运算
- YARN或将成为Hadoop新发力点
- Hadoop 1.x版本伪单机配置
- Glusterfs的编译选项 #pragma GCC poison system popen
- Python 大数据思维导图
- 大白话storm
- SQL优化(二) 快速计算Distinct Count
- O2O的万达模式:不玩电商玩大会员大数据