BitSet的python实现

15 篇文章 1 订阅
10 篇文章 0 订阅

Abstract

本文将介绍我如何将java中的BitSet实现(或者说叫翻译)为python版本, 保证了数据在java和python中的数据是互通和互操作的。最终的源代码在github javaCollectionInPython

背景和原因

事情是这样的,我们有一张event表格式它记录了不同aumation(1个automation可以理解为1组广告,1个template就理解为一个广告,1组广告中包含多个template),client_id是不同用户的id以及最后他们收到的时间。

idautomation_idtemplate_idclient_idtime
1111112022-09-01 10:10
2111112022-09-01 11:30
3121112022-09-01 11:00
4231112022-09-02 12:00
5232222022-09-02 12:03

我们可能会做如下的两类sql查询:

select automation_id, template_id, count(distinct(client_id)) from event where time > t1 and time < t2 group by automation_id,template_id

select automation_id, count(distinct(client_id)) from event where time > t1 and time < t2 group by automation_id

但是这个查询有个问题是数据库记录可能太多了,即便加上了合适的索引也还要20-30s才能查询回来。
所以想着就是能否预先归集一部分数据,因为过去已经产生的事件不会再改变。

所以想到了用java中的bitset来表示记录client_id这样也可以大幅缩减内存在用。然后将该bitset记录到数据库中新的聚合表中来缓存。 那么python中查询的时候就很方便直接查询该bitset然后进or 逻辑运算就可以了。

Java中的BitSet简单介绍

java中的bitset用一个long[] words数组来存储。 可以大幅压缩空间占用:
hashmap — 记录一个数字要一个int 4字节
boolean[] — 记录一个数字要1个字节
biset —记录一个数字要1个bit。
但是这个相比数组而言,应该算是以时间换空间。

在这里插入图片描述
当需要设置时:

wordIndex=inputIndex / 64
   不够则扩容
然后再设置这个long的某一位为1   

需要查询时与之类似。bitset在java中确实很方便,但是我们的backend有部分是python写的 怎么办呢?

如何在python中解析bitset呢并查询元素个数呢?

java中的bitset.cardinality() 方法可以返回bitset中有多少个元素。我们需要找到一个方法来在python中使用该bitset。
搜索了下python中的实现,用的比较多的是bitarray 但是这个有问题:

  1. 结构和java的bitset无法互通。
  2. 这个bitarray要求2个bitset操作时长度必须一致。
    所以就想着自己将java的bitset翻译到python中就可以了吧。

BitSet in Python

自己实现了一个python版本的bitset。该数据结构与java保持同样的数据结构,(底层的words结构一模一样)。我用了如下的方法:

第一步 初始的python代码

这个网站上将java bitset源码转成的python
在这里插入图片描述

修改代码

当然上面的代码肯定无法编译运行,所以还需要进行很多修改:

  1. 我删除了一些我不需要的方法 比如stream相关的方法。
  2. python没有long类型的数组 所以我用的numpy中的array来存储。
  3. 用np.bitwise 操作来提高性能 优化for循环
  4. 可能遇到int数值超过c最大值的问题.
  5. BitSet.valueOf() 构造函数使用的ByteBuffer相关类在python中没有,只有自己把主要提取long的操作翻译处理。 这里我删除了对big-endian机器的支持。
  6. System.arrayCopy在python中没有, 我用了np.append操作来替换。
  7. java的位运算操作不同。 比如 java的set方法中:1L << 456 在java中和python结果不同。
  8. 一些保留关键字的替换。比如or方法被替换为or_

最终实现

最后我实现的代码在这里

pip install javaCollectionInPython

可以用如下的方式来使用:

from javaCollectionInPython.bitset import BitSet

bs = BitSet.valueOf([])

bs.set(123, True)
bs.set(456, True)
# how many bits set?
print(bs.cardinality())

# 可以与另外一个bitset进行逻辑运算
bs.or_(bs2)

更多的例子在这里
这里面包含了如何在java中压缩bitset并且在python中解压缩并还原bitset的完整代码。

总结

bitset算是设计很精巧实现巧妙地那一类集合。核心代码可能在 300-400行左右。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值