位图法bitmap

概述:位图法bitmap

    位图法是用一个bit的0/1代表这个bit所在位置的值存在与否

    注:为什么不用boolean数组?
    根据《Java虚拟机规范》一书中的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。

    所以boolean数组,一个元素占用一个byte。

    同时,根据《Java虚拟机规范》的描述,用int数组是更合适的实现,不过byte实现比int实现更容易理解,所以这里选择byte实现。


代码如下:

package bitmap;

/**
 * 位图法
 * 1.位图法是用一个bit的0/1代表这个bit所在位置的值存在与否
 * 2.计算机的运算都是基于补码:
 * 2.1.正数的原码、反码、补码均相同;负数的反码除了符号位其它位均取反,补码为反码(不考虑符号位)加一
 * 2.2.补码转原码只需要对补码再求补码即可
 * 2.3.java没有无符号数(没有符号位的数为无符号数)
 * 2.4.java的按位与(&)、或(|)、异或(^)、非(~)都是基于补码运算的
 * 2.5.基于上面原则,直接用有符号数实现位图法时,需要对负数做额外处理
 * <p>
 * 例:a = -91, b = 27
 * 原码:11011011    00011011
 * 反码:10100100    00011011
 * 补码:10100101    00011011
 * a & b = 00000001 --补码转原码--> 00000001 = 1
 * a | b = 10111111 --补码转原码--> 11000001 = -65
 * a ^ b = 10111110 --补码转原码--> 11000010 = -66
 * ^a = 01011010 --补码转原码--> 01011010 = 90
 */
public class BitMap {
    private static final long MAX = Integer.MAX_VALUE;
    private static final long MIN = Integer.MIN_VALUE;
    private static final long SCOPE = MAX - MIN;
    private static final int SIZE = (int) (SCOPE / 8 + 1);
    private static final byte[] DIGIT = {1, 2, 4, 8, 16, 32, 64, -128};
    private static volatile byte[] nums = new byte[SIZE];
    private static volatile int size = 0;

    /**
     * 添加num
     * 当数据量比较小时,随机抽取一个num,大概率是不存在的,若此时先find(num),find(num)的时间大概率是被浪费了,所以直接往下走
     *
     * @param num 待添加的数
     */
    public static void add(int num) {
        long numLong = num;
        int index = (int) ((numLong - MIN) / 8);
        int offset = (int) ((numLong - MIN) % 8);
        byte tmp = nums[index];
        if (tmp == 0) {
            // 如果该byte为0,直接设值即可将对应bit设置为1
            nums[index] = DIGIT[offset];
            size++;
            return;
        }
        boolean isNavigative = false;
        // 处理数组中的数据,将负数转成正数
        if (tmp == -128) {
            isNavigative = true;
            tmp = 0;
        } else if (tmp < 0) {
            isNavigative = true;
            tmp = (byte) (-tmp);
        }

        if (offset == 7) {
            // 符号位的值变为1
            if (isNavigative) {
                // 负数的符号位已经为1,无需重复添加
                return;
            } else if (tmp == 0) {
                // 如果值为0,需要将符号位设置为1(-128)。--由于前面有对0的判断,所以不会走到该分支
                nums[index] = DIGIT[offset];
                size++;
            } else {
                // 对于正数,取相反数即可将符号位设置为1
                nums[index] = (byte) (-tmp);
                size++;
            }
        } else {
            // 对于不是存储在最高位的数,必然是正数,直接按位或,再根据数组中原始数据是否为负处理符号位
            int ori = tmp;
            tmp = (byte) (tmp | DIGIT[offset]);
            if (ori != tmp) {
                size++;
                if (isNavigative) {
                    nums[index] = (byte) (-tmp);
                } else {
                    nums[index] = tmp;
                }
            }
        }
    }

    /**
     * 移除num
     * 当数据量比较小时,随机抽取一个num,大概率是不存在的,所以先find(num),存在时再处理是比较省时间的
     *
     * @param num 待移除的数
     */
    public static void remove(int num) {
        if (!find(num)) {
            // 如果数不存在,跳过
            return;
        }
        size--;
        long numLong = num;
        int index = (int) ((numLong - MIN) / 8);
        int offset = (int) ((numLong - MIN) % 8);
        byte tmp = nums[index];
        if (offset == 7) {
            // 移除符号位的1
            if (tmp == -128) {
                // 只有符号位为1,直接设值为0
                nums[index] = 0;
            } else if (tmp < 0) {
                // 若为负数,取绝对值即可移除符号位的1
                nums[index] = (byte) (-tmp);
            } else {
                // 正数的符号位为0。--由于前面有判断是否有值,所以不会走到该分支
                return;
            }
        } else {
            // 移除非符号位
            if (tmp == -128) {
                // 只有符号位为1。--由于前面有判断是否有值,所以不会走到该分支
                return;
            } else if (tmp < 0) {
                // 小于0时,先转换成正数
                tmp = (byte) (-tmp);
                tmp -= DIGIT[offset];
                nums[index] = (byte) (-tmp);
            } else {
                tmp -= DIGIT[offset];
                nums[index] = tmp;
            }
        }
    }

    /**
     * 查找num是否存在
     *
     * @param num 带查找的数
     * @return true-存在/false-不存在
     */
    public static boolean find(int num) {
        long numLong = num;
        int index = (int) ((numLong - MIN) / 8);
        byte tmp = nums[index];
        if (tmp == 0) {
            // 无任何数
            return false;
        }
        int offset = (int) ((numLong - MIN) % 8);
        if (offset == 7) {
            // 若待查找的数在符号位,直接判断符号位的值
            return tmp < 0;
        } else {
            // 待查找的数不在符号位
            if (tmp == -128) {
                // -128只有符号位的值为1
                return false;
            } else if (tmp < 0) {
                // 其它负数需要先转换为正数
                tmp = (byte) (-tmp);
                // 按位与,若对应位置的值为1,则结果大于0
                return (tmp & DIGIT[offset]) > 0;
            } else {
                // 同上
                return (tmp & DIGIT[offset]) > 0;
            }
        }
    }


    /**
     * 返回bitmap中所有存在的数
     *
     * @return bitmap中所有存在的数
     */
    public static int[] list() {
        int[] allNum = new int[size];
        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                byte num = nums[i];
                int position = Integer.MIN_VALUE + 8 * i;
                index = addByteCount(allNum, index, position, num);
            }
        }
        return allNum;
    }

    /**
     * 解析byte中存储的数字
     *
     * @param allNum   所有数字
     * @param index    allNum中下一个数字的下标
     * @param position num对应的第一个数字
     * @param num      表示8个数字的一个byte
     * @return allNum中下一个数字的下标
     */
    private static int addByteCount(int[] allNum, int index, int position, byte num) {
        if (num == -128) {
            // 只有一个数字
            allNum[index] = position + 7;
            return ++index;
        }

        // 处理负数
        boolean isNavigative = false;
        int numValue;
        if (num < 0) {
            isNavigative = true;
            numValue = -num;
        } else {
            numValue = num;
        }
        // 处理符号位以外的bit
        int i = 0;
        while (numValue > 0) {
            if (numValue % 2 == 1) {
                allNum[index] = position + i;
                index++;
            }
            numValue /= 2;
            i++;
        }
        // 若符号位有数字,需要添加符号位的数字
        if (isNavigative) {
            allNum[index] = position + 7;
            index++;
        }
        return index;
    }

    /**
     * 返回bitmap中数字的个数
     *
     * @return 数字个数
     */
    public static int size() {
        return size;
    }

    public static void main(String[] args) {
        System.out.println(find(1));
        add(Integer.MIN_VALUE);
        add(Integer.MIN_VALUE);
        add(Integer.MIN_VALUE + 1);
        add(Integer.MIN_VALUE + 2);
        add(Integer.MIN_VALUE + 3);
        add(Integer.MIN_VALUE + 4);
        add(Integer.MIN_VALUE + 5);
        add(Integer.MIN_VALUE + 6);
        add(Integer.MIN_VALUE + 6);
        add(Integer.MIN_VALUE + 7);
        add(Integer.MIN_VALUE + 7);
        add(Integer.MIN_VALUE + 8);
        add(Integer.MIN_VALUE + 8);
        add(Integer.MIN_VALUE + 9);
        add(Integer.MIN_VALUE + 9);

        add(Integer.MAX_VALUE);
        add(Integer.MAX_VALUE);
        add(Integer.MAX_VALUE - 1);
        add(Integer.MAX_VALUE - 2);
        add(Integer.MAX_VALUE - 3);
        add(Integer.MAX_VALUE - 4);
        add(Integer.MAX_VALUE - 5);
        add(Integer.MAX_VALUE - 6);
        add(Integer.MAX_VALUE - 7);
        add(Integer.MAX_VALUE - 7);
        add(Integer.MAX_VALUE - 8);
        add(Integer.MAX_VALUE - 9);
        add(1);
        add(-1);
        add(0);
        add(3);
        add(-2313);
        add(334);
        System.out.println(size());
        System.out.println("-----list num start------");
        for (int i : list())
            System.out.println(i);
        System.out.println("-----list num end------");
        System.out.println(find(0));
        remove(100);
        remove(0);
        remove(Integer.MAX_VALUE);
        remove(Integer.MAX_VALUE);
        System.out.println(size());
        System.out.println(find(Integer.MAX_VALUE));
        System.out.println(find(Integer.MIN_VALUE));
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值