JAVA实现位图

本文介绍了一种使用JAVA实现位图的方法,通过将整数数组的每一位用于存储特定数值的出现状态,有效减少了存储空间的需求。文章详细解释了位图的初始化、元素添加、删除及查询操作,并提供了一个包含验证功能的示例代码。

JAVA实现位图

概述
JAVA中一个int占4字节,32位二进制数, 如果一个int整数32位二进制都表示一个数的话,那么一个整数能表示[0,31]
如果想表示[32,64]可以加入数组,让索引0位置的数表示[0,31],索引1表示[32,64],以此类推只要知道要存入的最大值,就可以实现一个比原来数组占用空间至少小32倍的数组 ,就是位图
位图的实现
1 . 初始化数组大小

假设N就是位图中的最大值,那么数组的长度就应该是N / 32 + 1
等同于 (N + 1) >> 5

2 . 加入元素

a . N >> 5 找到索引值
b . N % 32 余数就是索引值所在32位的具体位置,等同于N & 31
c . 让1 左移N &31位和数组索引位置值得二进制做个 | 运算

可能描述起来还不是特别清楚,直接上图吧!!
在这里插入图片描述
3 . 删除元素

删除元素和增加元素类似,在处理结果时让索引位置的值 & ~(1 << (N & 31))
就把这个位置上设置成0了

4 . 判断元素是否存在

这个是和add差不多的操作,查看N的索引N的位置的这个值是否为1
直接 (arr[val >> 5] & (1 << (val & 63))) != 0 即可

代码如下(带对数器)

package bitmap;

import java.util.HashSet;

/**
 * @Describe 实现位图
 * @Author 王超
 * @Version V1.0.0
 * @Date 2021/11/3 11:10
 */
public class BitMap {

    /**
     * 定义一个int数组,一个整数能表示32个数
     */
    private int[] arr;

    public BitMap(int max) {
        // 0 ~31
        // 32 ~ 63
        // 64 ~ 127
        // max / 32 + 1 = 数组长度
        arr = new int[(max + 32) >> 5];
    }

    /**
     * 1 . val >> 5 是因为找到索引位置 0~31是第0个索引位置,32~63是第一个索引位置 ,, 所以 /32 就是索引位置
     * 2 .  索引位置找到了,就应该找当前数在当前索引上处于32位的第几位就 =  val & 63 = val % 64
     * 3 . 1 << 是为了把这个二进制为无论0 1 都变成1
     * 4 . arr |= 是因为 ==> 如果之前这个索引位置上32位某一位被标记成1了,再来个数也在这个索引上,不仅这个数的32位对应的位要标称1还要不能改变之前的32位标记情况. 所以用|运算 arr[val << 5] |= 1 << (val & 63);
     * 例子 :0000 0000 0000 0000 0000 0000 0000 0000
     * add 个 0 变成 0000 0000 0000 0000 0000 0000 0000 0001
     * add 个 1 变成 0000 0000 0000 0000 0000 0000 0000 0011
     * add 个 2 变成 0000 0000 0000 0000 0000 0000 0000 0111
     * 例子过程 :
     * 1 . 加入0 ==> 先找到索引位置 == > 0 / 32 == 0
     * 2 . 锁定自己在32位的哪一位 == > 0 % 32 == 0
     * 3 . 把自己所在位标记成1 == > 000 ... 0001(1) << 0(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0001
     * 4 . 0000 0000 0000 0000 0000 0000 0000 0001 | 0000 0000 0000 0000 0000 0000 0000 0000 = 0000 0000 0000 0000 0000 0000 0000 0001
     * 5 . 加入0 ==> 先找到索引位置 == > 1 / 32 == 0
     * 6 . 锁定自己在32位的哪一位 == > 1 % 32 == 1
     * 7 . 把自己所在位标记成1 == > 000 ... 0001(1) << 1(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0010
     * 8 . 0000 0000 0000 0000 0000 0000 0000 0010 | 0000 0000 0000 0000 0000 0000 0000 0001 = 0000 0000 0000 0000 0000 0000 0000 0011
     *
     * @param val val
     */
    public void add(int val) {
        arr[val >> 5] |= 1 << (val & 63);
    }

    /**
     * 1 . val >> 5 是因为找到索引位置 0~31是第0个索引位置,32~63是第一个索引位置 ,, 所以 /32 就是索引位置
     * 2 .  索引位置找到了,就应该找当前数在当前索引上处于32位的第几位就 =  val & 63 = val % 64
     * 3 . 1 << 是为了把这个二进制为无论0 1 都变成1
     * 4 . ~取反是为了只把删除这个位变成0,其他位都是1
     * 4 . arr &= 是因为 ==> 原来位上可能有很多1 & 只有删除位是0其他位都是1 = 其他位是1还是1只有删除位变成0了
     * 例子 :0000 0000 0000 0000 0000 0000 0000 0000
     * add 个 0 变成 0000 0000 0000 0000 0000 0000 0000 0001
     * add 个 1 变成 0000 0000 0000 0000 0000 0000 0000 0011
     * add 个 2 变成 0000 0000 0000 0000 0000 0000 0000 0111
     * 例子过程 :
     * 1 . 加入0 ==> 先找到索引位置 == > 0 / 32 == 0
     * 2 . 锁定自己在32位的哪一位 == > 0 % 32 == 0
     * 3 . 把自己所在位标记成1 == > 000 ... 0001(1) << 0(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0001
     * 4 . 0000 0000 0000 0000 0000 0000 0000 0001 | 0000 0000 0000 0000 0000 0000 0000 0000 = 0000 0000 0000 0000 0000 0000 0000 0001
     * 5 . 加入0 ==> 先找到索引位置 == > 1 / 32 == 0
     * 6 . 锁定自己在32位的哪一位 == > 1 % 32 == 1
     * 7 . 把自己所在位标记成1 == > 000 ... 0001(1) << 1(val所在32位的位置) 得到 0000 0000 0000 0000 0000 0000 0000 0010
     * 8 . 0000 0000 0000 0000 0000 0000 0000 0010 | 0000 0000 0000 0000 0000 0000 0000 0001 = 0000 0000 0000 0000 0000 0000 0000 0011
     *
     * @param val val
     */
    public void delete(int val) {
        // 找到他把他改成0
        arr[val >> 5] &= ~(1 << (val & 31));
    }

    /**
     * 判断val是够存在位图中
     * 思路是: 1 << 到val的位上,用索引的这个值做 & 运算,这样除了val位上是1,其他位置全变成0了
     * 如果1这个位对应的也是1就返回非0数,否则返回0
     *
     * @param val val
     * @return Boolean
     */
    public boolean contains(int val) {
        return (arr[val >> 5] & (1 << (val & 63))) != 0;
    }

    public static void main(String[] args) {
        System.out.println("测试开始!");
        int max = 10000;
        BitMap bitMap = new BitMap(max);
        HashSet<Integer> set = new HashSet<>();
        int testTime = 10000000;
        for (int i = 0; i < testTime; i++) {
            int num = (int) (Math.random() * (max + 1));
            double decide = Math.random();
            if (decide < 0.333) {
                bitMap.add(num);
                set.add(num);
            } else if (decide < 0.666) {
                bitMap.delete(num);
                set.remove(num);
            } else {
                if (bitMap.contains(num) != set.contains(num)) {
                    System.out.println("异常!");
                    break;
                }
            }
        }
        for (int num = 0; num <= max; num++) {
            if (bitMap.contains(num) != set.contains(num)) {
                System.out.println("异常!");
            }
        }
        System.out.println("测试结束!");
    }


}

<think> 我们已经有用户提供的两个引用:[1]是关于Java位图的具体实现代码,[2]是关于位图法排序的使用方法和注意事项。 用户需求:查找使用Java实现位图算法的方法。 根据引用[1],我们有一个完整的位图实现类BitMap,包含构造函数、添加元素、删除元素和判断元素存在的方法。 根据引用[2],位图法通常用于存储数据,判断数据是否存在或重复,并且需要注意数据的最大值和数据的疏密程度(即稀疏性可能影响效率)。 因此,我们可以结合这两个引用,向用户介绍Java实现位图算法的方法,并解释关键代码。 步骤: 1. 介绍位图的概念和用途。 2. 展示完整的位图实现代码(基于引用[1]的代码)。 3. 解释构造函数、添加、删除和判断存在的方法。 4. 讨论位图算法的适用场景和局限性(参考引用[2])。 注意:用户要求使用中文回答,所以全部用中文。 另外,根据系统级指令,需要在回答中生成相关问题。 由于用户要求生成相关问题,我们在最后添加相关问题。 但是注意,用户要求“回答中引用的段落末尾自然地添加引用标识”,所以我们在解释关键点时,如果直接来自引用,需要添加引用标识。 由于引用[1]提供了代码和部分解释,引用[2]提供了适用场景和局限性,所以我们在相应地方添加引用标记。 开始回答: </think> ### Java实现位图算法的方法 位图(Bitmap)算法是一种高效存储和操作大量布尔值的数据结构,特别适合处理海量整数的去重、排序和存在性判断。以下是基于引用资料的Java实现详解: #### 1. **核心实现代码** ```java public class BitMap { private final long[] bitMap; // 使用long数组存储位数据(每个long存储64位) // 构造函数:初始化存储[0, max]范围的位图 public BitMap(int max) { bitMap = new long[(max + 64) >> 6]; // 等价于(max+64)/64 } // 添加元素:将指定数值对应的位设为1 public void add(int num) { bitMap[num >> 6] |= (1L << (num & 63)); // num>>6定位数组下标,num&63定位long中的位 } // 删除元素:将指定数值对应的位设为0 public void delete(int num) { bitMap[num >> 6] &= ~(1L << (num & 63)); } // 检查元素是否存在 public boolean contains(int num) { return (bitMap[num >> 6] & (1L << (num & 63))) != 0; } } ``` #### 2. **关键设计解析** 1. **存储结构** - 使用`long[]`数组(64位),每个元素存储64个布尔值,压缩空间利用率[^1]。 - 空间计算:存储范围`[0, max]`需$\lceil (max+1)/64 \rceil$个long,即`(max+64)>>6`。 2. **位运算技巧** - **定位数组下标**:`num >> 6`(等价于$num/64$),确定数值在哪个long中。 - **定位具体位**:`num & 63`(等价于$num \mod 64$),确定在long中的偏移量。 - **置位操作**:通过`1L << offset`生成掩码,配合`|=`或`&=`修改特定位。 #### 3. **算法特性** - **时间复杂度**:$O(1)$ 添加、删除、查询操作均为常数时间。 - **空间复杂度**:$O(n)$ 存储$n$个元素需约$n/64$个long(如存储100万数仅需15.6KB)[^2]。 - **局限性**: - 需提前知道数据最大值,稀疏数据空间利用率低[^2]。 - 仅适用于非负整数,浮点数需映射处理。 #### 4. **使用示例** ```java public static void main(String[] args) { BitMap bitmap = new BitMap(1000000); // 支持0~1000000的整数 bitmap.add(42); // 添加元素 bitmap.add(1000000); System.out.println(bitmap.contains(42)); // true System.out.println(bitmap.contains(999)); // false bitmap.delete(42); // 删除元素 System.out.println(bitmap.contains(42)); // false } ``` #### 5. **适用场景** 1. 大数据去重(如日志用户ID去重)[^2] 2. 快速排序(位图排序) 3. 布隆过滤器的底层实现 4. 数据库索引优化
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值