zoukankan      html  css  js  c++  java
  • Bitmap的原理和应用

    面试中经常会问到类似问题,看上去很简单,就是一个排序而已,但是你好好想想大部分排序算法都需要把数据放到内存里面操作,这10亿个数字得占用多少内存?好吧,你可以使用外部排序算法,在磁盘上完成排序!当然这些传统算法肯定是可以解决的,不过这里有一个更好的方案,采用bitmap排序,介绍如下:

    bitmap是什么? 大家都知道在计算机中一个字节(byte) = 8位(bit), 这里的bit就是位,数据的最小表示单位,map一般是表示地图或者映射,加一起叫作位图?貌似不太形象

    简单回顾一下二进制的一些知识:

    1byte = 8bit

    一个bit有2种状态,0 或者 1

    所以1个byte可以表示0000 0000 -> 1111 1111, 也就是十进制的 0 到 255。

    Bitmap介绍

    bitmap是很有用的结构。所谓的bitmap就是用一个bit位来标记某个元素,而数组下标是该元素。

    bitmap优势

    bitmap经常用在大数据的题中,比如10亿个int类型的数,如果用int数组存储的话,那么需要大约4G内存,浪费内存。如果用bitmap解决,就比较方便。bitmap可以用int来模拟,也可以用byte来模拟,它只是逻辑上的概念,在java语言中写不出来,我们采用byte。一个byte占8个bit,如果每一个bit的值是有或者没有,即1或0,则如下图所示:

    比如,当我们用 int 类型来模拟 Bitmap 时,一个 int 4个字节共 4*8 = 32位,可以表示32个数。原来10亿个 int 类型的数用 int 数组需要 4G 的内存,但是我用 bitmap 只需要 4GB / 32 = 128 MB 的内存,是不是少多了。

    bitmap代码实现

    第一步:构建特定长度的byte数组(new byte[capacity/8 + 1]),其中capacity为整数数组长度(如:10亿个数字等)

    byte[] bits = new byte[getIndex(n) + 1];

    第二步:计算数字num在byte[]中的位置(num/8和num >> 3一样),也就是说num在byte[k],算这个k是几

     /**
         * num/8得到byte[]的index
         * @param num
         * @return
         */
        public int getIndex(int num){
            return num >> 3;
        }

    第三步:计算数字num在byte[index]中的位置,就是在byte[index]的第几位,每个byte有8位(num % 8)

     /**
         * num%8得到在byte[index]的位置
         * @param num
         * @return
         */
        public int getPosition(int num){
            return num & 0x07;
        }

    第四步:将所在位置从0变成1,其它位置不变

    /**
         * 标记指定数字(num)在bitmap中的值,标记其已经出现过
         * 将1左移position后,那个位置自然就是1,然后和以前的数据做|,这样,那个位置就替换成1了
         * @param bits
         * @param num
         */
        public void add(byte[] bits, int num){
            bits[getIndex(num)] |= 1 << getPosition(num);
        }

    解释如下图:

     

    第五步:判断指定数字num是否存在

     /**
         * 判断指定数字num是否存在<br/>
         * 将1左移position后,那个位置自然就是1,然后和以前的数据做&,判断是否为0即可
         * @param bits
         * @param num
         * @return
         */
        public boolean contains(byte[] bits, int num){
            return (bits[getIndex(num)] & 1 << getPosition(num)) != 0;
        }
     

    第六步:重置某一数字对应在bitmap中的值

     /**
         * 重置某一数字对应在bitmap中的值<br/>
         * 对1进行左移,然后取反,最后与byte[index]作与操作。
         * @param bits
         * @param num
         */
        public void clear(byte[] bits, int num){
            bits[getIndex(num)] &= ~(1 << getPosition(num));
        }

    全部代码如下:

    public class Test {
    
        /**
         * 创建bitmap数组
         */
        public byte[] create(int n){
            byte[] bits = new byte[getIndex(n) + 1];
            
            for(int i = 0; i < n; i++){
                add(bits, i);
            }
            
            System.out.println(contains(bits, 11));
            
            int index = 1;
            for(byte bit : bits){
                System.out.println("-------" + index++ + "-------");
                showByte(bit);
    
            }
            
            return bits;
        }
        
        /**
         * 标记指定数字(num)在bitmap中的值,标记其已经出现过<br/>
         * 将1左移position后,那个位置自然就是1,然后和以前的数据做|,这样,那个位置就替换成1了
         * @param bits
         * @param num
         */
        public void add(byte[] bits, int num){
            bits[getIndex(num)] |= 1 << getPosition(num);
        }
        
        /**
         * 判断指定数字num是否存在<br/>
         * 将1左移position后,那个位置自然就是1,然后和以前的数据做&,判断是否为0即可
         * @param bits
         * @param num
         * @return
         */
        public boolean contains(byte[] bits, int num){
            return (bits[getIndex(num)] & 1 << getPosition(num)) != 0;
        }
        
        /**
         * num/8得到byte[]的index
         * @param num
         * @return
         */
        public int getIndex(int num){
            return num >> 3;
        }
        
        /**
         * num%8得到在byte[index]的位置
         * @param num
         * @return
         */
        public int getPosition(int num){
            return num & 0x07;
        }
        
        /**
         * 重置某一数字对应在bitmap中的值<br/>
         * 对1进行左移,然后取反,最后与byte[index]作与操作。
         * @param bits
         * @param num
         */
        public void clear(byte[] bits, int num){
            bits[getIndex(num)] &= ~(1 << getPosition(num));
        }
        
        /**
         * 打印byte类型的变量<br/>
         * 将byte转换为一个长度为8的byte数组,数组每个值代表bit
         */
        
        public void showByte(byte b){
            byte[] array = new byte[8];
            for(int i = 7; i >= 0; i--){
                array[i] = (byte)(b & 1);
                b = (byte)(b >> 1);
            }
            
            for (byte b1 : array) {
                System.out.print(b1);
                System.out.print(" ");
            }
            
            System.out.println();
        }
        
        public static void main(String[] args) {
            int n = 100;
            new Test().create(n);
        }
    }
    欢迎关注微信公众号:大数据从业者
  • 相关阅读:
    Working with WordprocessingML documents (Open XML SDK)
    How to Choose the Best Way to Pass Multiple Models in ASP.NET MVC
    Azure:Manage anonymous read access to containers and blobs
    Convert HTML to PDF with New Plugin
    location.replace() keeps the history under control
    On the nightmare that is JSON Dates. Plus, JSON.NET and ASP.NET Web API
    HTTP Modules versus ASP.NET MVC Action Filters
    解读ASP.NET 5 & MVC6系列(6):Middleware详解
    Content Negotiation in ASP.NET Web API
    Action Results in Web API 2
  • 原文地址:https://www.cnblogs.com/felixzh/p/15746669.html
Copyright © 2011-2022 走看看