文章目录
java自带的bitset只提供了基本的功能,不算很强大。所以实际开发中要借助其他类库,例如RoaringBitmap。
RoaringBitmap在方法的丰富性上,以及性能上都有提高。
为什么会提到这个话题呢? 是因为项目中有统计功能涉及到这块。
一条记录可能有多个标签,用in是不行的。因为in适合的情况是多条记录,每条记录一个值。
这里是一条记录几个值,用find_in_set()效率也不满足,所以才想到位图算法。
jdk自带bitSet
bitSet示例
bitSet比较主流,示例很多:
@Slf4j
public class BitSetDemo {
public static void main(String[] args) {
// 1 小明
// 2 小刚
// 3 建军
// 4 小红
// 1000 小花
// 2000 小丽
BitSet appleBitSet = new BitSet(); // appleBitSet
appleBitSet.set(4);
appleBitSet.set(1000);
BitSet huaweiBitSet = new BitSet(); // huaweiBitSet
huaweiBitSet.set(1);
huaweiBitSet.set(2);
BitSet vivoBitSet = new BitSet(); // vivoBitSet
vivoBitSet.set(2000);
BitSet maleBitSet = new BitSet(); // maleBitSet
maleBitSet.set(1);
maleBitSet.set(2);
maleBitSet.set(3);
BitSet femaleBitSet = new BitSet(); // femaleBitSet
femaleBitSet.set(4);
femaleBitSet.set(1000);
BitSet appleMaleSet = (BitSet)appleBitSet.clone();
// 求 苹果的女性
appleMaleSet.and(femaleBitSet);
log.info("用苹果的女性={}",appleMaleSet);
// 小明是否用苹果
boolean b = appleBitSet.get(1);
log.info("小明是否用苹果={}",b);
// 清除appleBitSet的第一位 (应该是清楚第4位才可以)
appleBitSet.clear(4);
appleBitSet.flip(4); // 反转
log.info("清除appleBitSet的第一位,appleBitSet={}",appleBitSet);
// false和无值的区别
}
}
bitset常用方法
1、set设置值。
2、flip() 反转,true变false,false变true。
3、clear() 清除,相当于设置为false。
4、and() 且操作。(因为会改变原集合,所以要先clone,用clone的进行and操作)
5、or() 或操作。
注:bitmap的or不能直接实现,但是bitset也支持了,很方便。
bitset打印出来是怎样的,实际类似于json,例如:{1,2,3,100,2000}。所以无论放到数据库还是文件,存取都比较方便的。
存到数据库还是文件里
当数据量很大时,bitmap会很大,存到哪里成为了一个问题。
存文件吧,主要是io流的开销也挺大,尤其是需要频繁读写的时候。
存数据库吧,因为数据太大,效率也不高。
频繁修改的数据,最好借助内存。
RoaringBitmap
maven引入
第2个是roaringbitmap,但是一块贴上了啊。
<dependency>
<groupId>uk.gov.gchq.gaffer</groupId>
<artifactId>bitmap-library</artifactId>
<version>1.21.1</version>
</dependency>
<dependency>
<groupId>org.roaringbitmap</groupId>
<artifactId>RoaringBitmap</artifactId>
<version>0.9.22</version>
</dependency>
其他可用的RoaringBitmap版本,如果0.9.20、0.8.12等,都可以参考下。
RoaringBitmap 基本用法,包括文件流
public static void main(String[] args) throws Exception{
String filePath="d://bit.txt";
RoaringBitmap roaringBitmap = new RoaringBitmap ();
roaringBitmap.add(1);
roaringBitmap.add(20000);
serialize(roaringBitmap,filePath);
RoaringBitmap deserialize = deserialize(filePath);
// RoaringBitmap 最大的特点就是 带有压缩,例如 添加一个很大的数,基数不会跳跃
logger.info("getCardinality:{}",deserialize.getCardinality());
}
// 序列化 保存
public static void serialize(RoaringBitmap roaringBitmap,String filePath) throws IOException {
roaringBitmap.serialize(new DataOutputStream(new FileOutputStream(filePath)));
}
// 反序列化 读取
public static RoaringBitmap deserialize(String filePath) throws IOException {
RoaringBitmap roaringBitmap = new RoaringBitmap ();
roaringBitmap.deserialize(new DataInputStream(new FileInputStream(filePath)));
return roaringBitmap;
}
这段代码实测少类
这个代码不行啊,0.9.20和0.9.22版本都拿到了。是不是MutableRoaringBitmap太高级了。
import org.roaringbitmap.RoaringBitmap;
import org.roaringbitmap.buffer.ImmutableRoaringBitmap;
import org.roaringbitmap.buffer.MutableRoaringBitmap;
public class EWAHCompressedBitmapExample {
public static void main(String[] args) {
// 创建一个可变的RoaringBitmap
MutableRoaringBitmap mutableBitmap = new MutableRoaringBitmap();
// 添加值
mutableBitmap.add(1);
mutableBitmap.add(2);
mutableBitmap.add(3);
// 转换为不可变的RoaringBitmap
ImmutableRoaringBitmap immutableBitmap = mutableBitmap.toImmutableRoaringBitmap();
// 检查值是否存在
boolean contains1 = immutableBitmap.contains(1); // true
boolean contains4 = immutableBitmap.contains(4); // false
// 输出基本信息
System.out.println("Cardinality: " + immutableBitmap.getCardinality()); // 输出3
System.out.println("Contains 1: " + contains1);
System.out.println("Contains 4: " + contains4);
}
}
其他工具类
还有很多,如:
ArrayContainer
RunContainer
搜索算法数据库设计
数据库设计可以为
t_bitset
category 大类(如性别,设备类型等)
type 类型(如男、女,android、ios等)
fileName 文件名
filePath 文件路径
倒排索引
这个概念一定不陌生。
常规的存储形式。
一条记录 多个标签。
倒排索引。
一个标签 多条记录的id集合。
倒排索引在elastic search中就有集成。