Java实现基于bitmap的字符串去重统计

字符串与整数不同,不能直接用位图表示。本文介绍两种基于bitmap思想的字符串去重统计方法:布隆过滤器和哈希映射法。

## 方法一:布隆过滤器(Bloom Filter)

布隆过滤器是bitmap的扩展应用,它使用多个哈希函数,将每个字符串映射到bitmap的多个位置上。

```java
import java.util.BitSet;
import java.util.function.Function;

public class BloomFilter {
    private BitSet bitmap;
    private int numBits;
    private int numHashes; // 哈希函数数量
    
    // 使用不同的种子计算哈希
    private int hash(String str, int seed) {
        int h = str.hashCode();
        // 使用简单的混合函数与seed结合
        return Math.abs((h ^ seed) % numBits);
    }
    
    public BloomFilter(int size, int hashes) {
        this.numBits = size;
        this.numHashes = hashes;
        this.bitmap = new BitSet(size);
    }
    
    // 将字符串添加到过滤器
    public void add(String str) {
        for (int i = 0; i < numHashes; i++) {
            int pos = hash(str, i);
            bitmap.set(pos);
        }
    }
    
    // 检查字符串是否可能在集合中
    // 返回false表示一定不在,返回true表示可能在
    public boolean mayContain(String str) {
        for (int i = 0; i < numHashes; i++) {
            int pos = hash(str, i);
            if (!bitmap.get(pos)) {
                return false; // 如果有任一位为0,则字符串一定不在集合中
            }
        }
        return true; // 所有位都为1,字符串可能在集合中
    }
    
    // 估计集合中元素数量
    // m = 位数,k = 哈希函数数量,X &

<think>嗯,用户想处理10亿个手机号的问题。首先,我得考虑数据量有多大。每个手机号是11位,如果用字符串存储的话,每个号码大概占11字节,那么10亿个就是110亿字节,也就是大约102.3GB。这显然没法直接放进内存里处理,所以必须用更高效的方法。 想到之前学过的BitMap,也就是位图。这种方法通过位来标记存在性,可以大大节省空间。比如,手机号一般是1开头,后面跟着10位数字。如果只考虑后9位的话,可能有不同的组合,但需要计算一下位数。比如,后9位的范围是从000000000到999999999,总共10^9个可能的数值。这样的话,位图需要10亿位,也就是大约119MB(10^9 bits / 8 / 1024 / 1024)。这样的话,内存里放得下,速度也快。 不过,用户提到的是10亿个手机号,可能有很多复。这时候BitMap就能高效地判断是否已经存在,每个号码只需计算对应的位是否为1。但问题是手机号可能有11位,全部用BitMap的话,可能需要更大的空间。比如,如果处理全部11位的话,数值范围是10^10,也就是需要100亿位,大约1.16GB。这还是可行的,因为现代服务器的内存足够处理这个大小。 另外,分桶的方法也是一种选择。比如,根据手机号的前几位分成不同的文件,每个文件单独处理。比如前三位作为分桶的依据,这样每个桶里的号码数量会减少,可以逐个处理,避免内存不足的问题。但这种方法需要多次磁盘IO,可能会影响速度,但适合分布式处理。 还有可能用户需要考虑的是哈希表,但哈希表存储10亿个手机号的话,即使用Long类型,每个号码占8字节,10亿个就是80亿字节,大约7.45GB,这比BitMap大很多,内存可能不够。所以BitMap更优。 另外,引用中提到的分桶理论,比如将手机号的前两位装入内存,后八位用BitSet处理。例如,前两位可能有99种可能(因为第一位是1,第二位可能是3到9),每个前两位对应一个BitSet,存储后8位的存在性。每个BitSet需要99999999位,大约11.92MB,总共有99个这样的BitSet,总共约1.16GB,加上索引结构,可能在内存中可行。 所以综合来看,BitMap是最直接有效的方法,尤其是处理全部11位可能需要较大的位图,但可以通过分桶来优化内存使用。或者,使用分段处理,比如将手机号分成不同的段,每段对应不同的位图,从而减少单个位图的大小。 此外,还要考虑实际应用中手机号的分布情况。比如,可能某些号段未被使用,或者号码分布不均匀,这时候分桶可以更有效地利用内存。同时,使用高效的哈希函数或直接转换手机号为数值,可以加快处理速度。 最后,需要验证BitMap实现方式,比如在Java中使用BitSet类,或者自己实现位操作,确保能够正确映射手机号到位图中的位置,并且处理可能的冲突或错误。</think>对10亿规模的手机号进行高效,需结合数据结构优化与分治策略。以下是分步骤解决方案: --- ### **一、核心算法选择** 采用**BitMap(位图)**数据结构[^1][^3],其特点为: 1. 空间效率:仅用1个bit表示一个数值存在性,相比传统HashSet(每个元素占用32-64bit)节省98%以上空间 2. 时间复杂度:插入和查询操作均为$O(1)$ 3. 数学表达:手机号$n$对应位图索引位置计算为$$index = n - minPhoneNumber$$ --- ### **二、具体实现步骤** #### **1. 数值范围分析** 中国手机号特征: - 固定1开头(11位数字) - 有效范围:$1.3\times10^{10}$至$1.9\times10^{10}$(即13xxxxxxxxx至19xxxxxxxxx) - 理论总数量:$6\times10^9$(实际已发放约16亿) #### **2. BitMap容量计算** - 最大需要覆盖数值范围:$1.9\times10^{10} - 1.3\times10^{10} = 6\times10^9$ - 位图大小:$6\times10^9 \text{ bits} = 715 \text{ MB}$ ```python # 位图初始化示例 bitmap_size = 6 * 10**9 // 8 + 1 # 单位转换为字节 bitmap = bytearray(bitmap_size) ``` #### **3. 分桶优化策略** 当单机内存不足时,采用**分桶位图**: ```java // 示例:按前3位分桶(100个桶) String phone = "13812345678"; int bucket = Integer.parseInt(phone.substring(0,3)) - 130; // 前3位130-199对应0-69 BitSet bitmap = buckets[bucket]; // 每个桶处理后8位 ``` --- ### **三、性能对比** | 方法 | 内存消耗 | 时间复杂度 | 实现复杂度 | |--------------|----------|------------|------------| | HashSet | ≥40 GB | $O(1)$ | 低 | | 排序+遍历 | ≥8 GB | $O(n\log n)$ | 中 | | **BitMap** | **715 MB** | **$O(n)$** | **高** | --- ### **四、工程实践建议** 1. **流式处理**:采用分批次读取(如每次100万条)+ BitMap标记 2. **分布式扩展**:按手机号前N位分片到不同计算节点 3. **异常处理**:增加手机号格式校验模块 4. **验证测试**:使用已知数据集验证准确率 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值