一个32g的内存操作系统,在20亿个整数,找出某个数x是否存在其中
- 方式一:假设是java int占4个字节,1个字节=8位(1byte=8bit)一个int 32*20亿 个bit 约等于7g
- 方式二:不存储具体数据,而存储是否存在,如果存在则打上标签,采用bit存储20亿个数就是20亿位,空间就是0.2g。
什么是Bitmap Bit-map就是用一个bit位来标记某个元素对应的Value(若元素存在bit位置为1,不存在则置为0)。可创建一个整型数组(如byte数组,int数组,long数组)来表示
Bitmap原理 在Java中,数据类型int占4字节,4字节=32位(1 byte = 8 bit) 数据类型byte占1字节,1字节=8位
需求案例方式二解答
需要申请的数组大小
array[0]:可表示0~31
array[1]:可表示32~63
array[2]可表示64~95
…
总的数组长度为20亿/32 +1
比如元素存储 80,确定所在数组的bit位置
1、数组index索引 80/32 = 2.5,即第3个数组的位置 arr[2]
2、比特位index索引 80%32 = 16,索引下标为16的比特位,把比特位设置为1,即arr[2][16]
注意
缺点
bitmap
会有碰撞问题,即可能映射到同个位置,即hash碰撞业务应用:日活/月活UV统计、签到统计、用户点赞,用户签到,访问计数,在线用户数等
位图在java里面的实现BitSet类
是一个实现按需增长的位向量,位Set的每一个位置都有一个boolean值,默认初始值都是false
底层实现是使用long数组作为内部存储结构的,所以BitSet的大小为long类型大小(64位)的整数倍
如果指定了bitset的初始化大小,会规整到一个大于或者等于这个数字的64的整倍数(内存对齐)
主要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 的位数
API测试
public static void testBitSet(){
BitSet bitSet = new BitSet();
bitSet.set(0);
bitSet.set(66);
System.out.println(bitSet.size()); // 128
System.out.println(bitSet.length()); // 67
System.out.println(bitSet.cardinality()); // 2
System.out.println("=====");
System.out.println(bitSet.get(0)); // true
System.out.println(bitSet.get(1)); // false
}
解答思路
海量数据 里面查找是否存在,排序,交集,并集等,这类题目基本就是使用位图解决
这类题目一般有两个面试形式
方式二 上机编码形式
public class BitSetTest { public static void main(String[] args) { // BitSet bitSet = new BitSet(); // bitSet.set(0); // bitSet.set(66); // System.out.println(bitSet.size()); // System.out.println(bitSet.length()); // System.out.println(bitSet.cardinality()); // System.out.println(bitSet.get(0)); // System.out.println(bitSet.get(1)); testBitSetMap(); }
public static void testBitSetMap(){
//范围
int range=100000000;
//个数
int total=10000000;
//普通
ArrayList<Object> list = new ArrayList<>();
//声明一个位图
BitSet bitSet = new BitSet(range);
//产生随机数
for (int i = 0; i < total; i++) {
int random = (int) (Math.random() * range);
bitSet.set(random);
list.add(random);
}
System.out.println("产生的随机数:"+list);
System.out.println("bitSet是一的个数:"+bitSet.cardinality());
System.out.println("bitSet的大小:"+bitSet.size());
System.out.println("bitSet最高位加1 length:"+bitSet.length());
//遍历bitst,没有出现的打印出来
for (int i = 0; i <range; i++) {
if(!bitSet.get(i)) {
System.out.print(i+"");
}
}
}
} ```
简介: 限定优惠劵业务解决方案-Redis的BitMap应用
需求背景
题目条件
老王
老帆
docker部署redis7
#部署
docker run -itd --name xdclass-redis1 -p 6379:6379 -v /mydata/redis/data:/data redis:7.0.8 --requirepass 123456
#进入容器内部
docker exec -it 容器id /bin/bash
#客户端连接
./redis-cli
#授权
auth 123456
冰冰
存在高并发场景,频繁插入查询数据库不行,那可以结合Redis,进行判断
但数据结构应该采用bitmap数据结构,每个用户id占用空间只占用1位
领过的用户把user_id加入bitmap中,下次如果再次领取的查询是否重复领取
Redis的bitmap
Redis中提供的BitMap命令:setbit,getbit,bitcount
领劵:setbit coupon-id user-uid 1
例子:setbit coupon-id:876 8888 1
已经领券判断:getbit coupon-id user-uid
例子:getbit coupon-id:876 8888
如果未领取状态是0,如果已领就是1
统计该优惠券有多少个用户领取
bitcount coupon-id:876
返回值为该key值中1的个数
分析
注意
简介:进阶版哈希表BloomFilter
前置环境准备
项目开发
依赖包引入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
</dependencies>
数据准备 (随机生成500万URL)
@Test
public void testGeneUrl() {
try{
File file = new File("/Users/xdclass/Desktop/dat.txt");
if (!file.exists()) {
file.createNewFile(); // 创建新文件,有同名的文件的话直接覆盖
}
FileOutputStream fos = new FileOutputStream(file, true);
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(osw);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 5000000; i++) {
String name = RandomStringUtils.randomAlphabetic(5);
String fileName = "https://www." + name + ".com" + i + "\n";
builder.append(fileName);
}
bw.write(String.valueOf(builder));
bw.newLine();
bw.flush();
bw.close();
osw.close();
fos.close();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
}
}
Guava包布隆过滤器介绍
//参数一: 指定布隆过滤器中存的是什么类型的数据,有 IntegerFunnel,LongFunnel,StringCharsetFunnel
//参数二: 预期需要存储的数据量
//参数三: 误判率,默认是 0.03
BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")), 5000000, 0.01);
核心代码编写
@Bean
public Set set() throws IOException {
Set<String> set = new LinkedHashSet<>();
FileInputStream inputStream = new FileInputStream(new File("/Users/xdclass/Desktop/dat.txt"));
InputStreamReader streamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(streamReader);
String line = null;
while (true) {
line = reader.readLine();
if (line != null) {
set.add(line);
} else {
break;
}
}
inputStream.close();
return set;
}
@Bean
public BloomFilter bloomFilter() throws IOException {
BloomFilter bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")), 5000000, 0.01);
FileInputStream inputStream = new FileInputStream(new File("/Users/xdclass/Desktop/dat.txt"));
InputStreamReader streamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(streamReader);
String line = null;
while (true) {
line = reader.readLine();
if (line != null) {
bloomFilter.put(line);
} else {
break;
}
}
inputStream.close();
return bloomFilter;
}
@RestController
@RequestMapping("/api")
public class FilterController {
@Autowired
private BloomFilter<String> bloomFilter;
@Autowired
private Set set;
@GetMapping("/bloom")
public String list() throws IOException {
//判断是否包含这个内容
if (bloomFilter.mightContain("https://www.dhVrX.com5")) {
return "命中了";
} else {
return "没命中";
}
}
@GetMapping("/set")
public String set() {
if (set.contains("httssps://www.shncb.com999663")) {
return "命中了";
} else {
return "没命中";
}
}
}