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); } }