1.海量数据去重-BitMap位图解决方案
-
需求(面试题)
-
一个32位4G内存的操作系统,在20亿个整数,找出某个数X是否存在其中
-
假如是java语言,int占4字节,1字节=8位(1 byte = 8 bit)
-
方式一:每个数字是int类型存储,就是20亿个int需要的空间大小就是 7GB多
-
方式二:不存储具体数据,而存储是否存在,如果存在则位上存储1,采用bit位存储20亿个数就是20亿位,空间是0.2GB多
-
-
什么是BitMap位图
- 本质上是哈希表的一种应用实现,原理简单,以 bit 为单位构建数组的方案,就叫作 Bitmap,翻译为位图。即bit 的集合
- 使用一个bit表示状态, 两种状态 (0不存在和1存在) 使用最少字节的类型来定义数组,即最小的空间存储数据标识
-
注意
- 位图适合对【数值类型】的海量数据进行查询统计、排序、去重 和 对两个集合做交集、并集运算
- bitmap在数据连续的时候,非常节省空间,但是在数据稀疏的时候,会有极大的浪费
-
缺点
- 数据碰撞:
- 字符串映射到
bitmap
会有碰撞问题,即可能映射到同个位置,即hash碰撞
- 字符串映射到
- 稀疏数据
- 不连续的数据容易浪费空间,比如存入1和88两个数,需要构建长度89的数组
- 表示索引从1到88,所以需要构建一个长度为89的数组,存放1到88的元素,但实际只存储2个数字
- 如果用户的ID的数据类型是int32的话,那么最大值是2^32,需要用512MB的字节的位图来表示
- 2^32bit=4294967296 比特(bit)=512 兆字节(MB)
- 如果只往bitmap存储一个最大值,那边需要申请512 兆字节(MB),大大浪费空间
- 数据碰撞:
-
业务应用:日活/月活UV统计、签到统计、用户点赞,用户签到,访问计数,在线用户数等
2.编码实现20亿个数据,找出某个数X是否存在其中
-
题目需求
-
20亿个数据,找出某个数X是否存在其中
-
前提条件:使用java现有数据结构或自定义数据结构,要求高效和省空间
-
-
位图在java里面的实现BitSet类
-
是一个实现按需增长的位向量,位Set的每一个位置都有一个boolean值,默认初始值都是false
-
底层实现是使用long数组作为内部存储结构的,所以BitSet的大小为long类型大小(64位)的整数倍
-
如果指定了bitset的初始化大小,会规整到一个大于或者等于这个数字的64的整倍数(内存对齐)
- 比如64位,bitset的大小是1个long,而65位时,bitset大小是2个long,即128位
-
-
主要的API
void and(BitSet set) 对此目标位 set 和参数位 set 执行逻辑与操作。
void or(BitSet set) 对此目标位集执行逻辑或操作
void clear() 将此 BitSet 中的所有位设置为 false
void clear(int bitIndex):将指定索引处的位设置为 false
void set(int index) 将指定索引处的位设置为 true
boolean get(int index) 返回指定索引处的位值
int size():返回此 BitSet 中的位数(按逻辑大小)【表示位值时实际使用空间的位数,值是64的整数倍】
int length() 返回此 BitSet 的"逻辑大小",BitSet 中最高设置位的索引加 1
int cardinality() 返回此 BitSet 中设置为 true 的位数
-
解答思路
-
海量数据 里面查找是否存在,排序,交集,并集等,这类题目基本就是使用位图解决
-
给定20亿个不重复的 int的整数,再给一个数,如何快速判断这个数是否在那X亿个数当中
-
解法:遍历X亿个数字,映射到BitMap中,对于给出的数,直接判断指定的位上存在不存在即可
-
-
编码实现
public class BitSetTest {
public static void main(String[] args) {
int targetNum = 39;
boolean flag = seekNum(targetNum);
System.out.println("当前数值是否在集合中:"+flag);
}
public static boolean seekNum(int targetNum){
//定义数据生成范围
int numLength = 2000000000;
//定义位图
BitSet bitSet = new BitSet(numLength);
//随机生成数字
for (int i = 1; i <= numLength; i++) {
int random = (int) (Math.random() * numLength);
bitSet.set(random);
}
//输出基本信息
System.out.println("bitMap是1的个数:"+bitSet.cardinality());
System.out.println("bitMap的size:"+bitSet.size());
System.out.println("bitMap的length:"+bitSet.length());
//判断数值是否存在
return bitSet.get(targetNum);
}
}