引言

阿里内推面试的时候被考了一道编程题:10亿个范围为1~2048的整数,将其去重并计算数字数目。

我看到这个题目就想起来了《编程珠玑》第一章讲的叫做BitMap的数据结构,但是我并没有在java上实现过,这就比较尴尬了,再加上时间不多了,只好暂时用byte代替bit,浪费7个字节,在这篇文章里总结一下BitMap的常用代码,以免重蹈覆辙。

偷懒的方法

其实java.util包中已经有了一个实现,可以用这个数据结构偷懒,写了一个Demo如下:

package org.du.offerproblem.bitmap;

import java.util.BitSet;

/**

* Created by 燃烧杯 on 2018/2/24.

*/

public class BitSetTest {

public static void main(String[] args) {

int [] array = new int [] {1,2,3,22,0,3,63};

BitSet bitSet = new BitSet(1);

System.out.println(bitSet.size()); //64

bitSet = new BitSet(65);

System.out.println(bitSet.size()); //128

bitSet = new BitSet(23);

System.out.println(bitSet.size()); //64

//将数组内容组bitmap

for(int i=0;i

{

bitSet.set(array[i], true);

}

System.out.println(bitSet.get(22));

System.out.println(bitSet.get(60));

System.out.println("下面开始遍历BitSet:");

for ( int i = 0; i < bitSet.size(); i++ ){

System.out.println(bitSet.get(i));

}

}

}

java.util.BitSet的底层是long数组,.size()方法返回的是BitSet当前位数,因为long是64位的,所以size返回的值也是64的整数倍,所以在上面的代码中发现,我在构造函数中传入初始化长度1~64中的任意一个值,size的大小都是64位,因为此时long数组的长度只有1,而我一旦将其设置成65,size的大小就变成128了。

用这个类是个偷懒的好办法,但是一旦面试官一定要让你自己实现一个就不行了。

自己实现BitMap

可以用int数组来实现一个BitMap,这种方法最关键的是求出index在int数组中的位置以及在该位置上的偏移量,有如下公式:

int数组中的位置(belowIndex) = (index - 1) >> 5

偏移量(offset) = (index - 1) & 31

我们这里假设index是从1开始的,所以先将index减去1,如果你要统计的数据范围是从0开始的,则不需要减去这个1。右移5位(相当于除以32)的原因是,一个int型数据是32位的(2的5次方等于32)。偏移量中&31相当于模32,其原因也因为int型数据是32位的。如果你不准备基于int,而是准备基于其他的,如byte,long的话,(以byte为例)则将>>5改成>>3,&31改成&7即可。

setBit的流程如下:

求出belowIndex并且得到int值;

求出offset并且利用“或运算”将刚才得到的int值的offset位置置为1;

getBit的流程如下:

求出belowIndex并且得到int值;

求出offset,之后利用“与运算”取出offset位置的值将其变为01后返回;

代码如下:

package org.du.offerproblem.bitmap;

/**

* 实现BitMap

*注:这个bitMap的index是从1开始的

*/

public class BitMap {

private long length;

private static int[] bitsMap;

//构造函数中传入数据中的最大值

public BitMap(long length) {

this.length = length;

// 根据长度算出,所需数组大小

bitsMap = new int[(int) (length >> 5) + ((length & 31) > 0 ? 1 : 0)];

}

public int getBit(long index) {

int intData = bitsMap[(int) ((index - 1) >> 5)];

int offset = (int) ((index - 1) & 31);

return intData >> offset & 0x01;

}

public void setBit(long index) {

// 求出该index - 1所在bitMap的下标

int belowIndex = (int) ((index - 1) >> 5);

// 求出该值的偏移量(求余)

int offset = (int) ((index - 1) & 31);

int inData = bitsMap[belowIndex];

bitsMap[belowIndex] = inData | (0x01 << offset);

}

public static void main(String[] args) {

BitMap bitMap = new BitMap(32);

bitMap.setBit(32);

System.out.println(bitMap.getBit(1));

System.out.println(bitMap.getBit(32));

}

}

使用BitMap进行数据去重

下面给出数组去重的代码:

package org.du.offerproblem.bitmap;

import java.util.Arrays;

/**

* Created by 燃烧杯 on 2018/2/24.

* 这个BitMap的去重是从0开始

*/

public class BitMapRepRemove {

//public static final int _1MB = 1024 * 1024;

//public static byte[] flags = new byte[ 512 * _1MB ];

public static byte[] flags;

public static void main(String[] args) {

int[] array = {255, 1024, 1024, 0, 65536, 0, 1024, 8888, 9999, 1111, 8888};

int length = 65536 + 1;

flags = new byte[(int) (length >> 3) + ((length & 7) > 0 ? 1 : 0)];

int index = 0;

for(int num : array) {

if( getFlags(num) != 1) {

//未出现的元素

array[index] = num;

index = index + 1;

//设置标志位

setFlags(num);

}

}

array = Arrays.copyOf(array, index);

System.out.println(Arrays.toString(array));

System.out.println(array.length);

}

public static void setFlags(int num) {

int offset = num & (0x07);

flags[num >> 3] |= 0x01 << offset;

}

public static int getFlags(int num) {

int offset = num & (0x07);

return flags[num >> 3] >> offset & 0x01;

}

}

java bitmap jar_Java面试中常用的BitMap代码相关推荐

  1. java程序员面试中的5个杀手锏问题

    java程序员面试中的5个杀手锏问题,不管你去面试的频率如何,下面这五个问题是每个软件工程师都应该问的--将有助于你确定自己在这家公司长期工作是否会合作愉快. 你们的企业文化是什么? 你每天将会有10 ...

  2. Java输入/输出流体系中常用的流分类

    java输入/输出流体系中常用的流分类 分类 字节输入流 字节输出流 字符输入流 字符输出流 抽象基类 InputStream OutputStream Reader Writer 访问文件 File ...

  3. 面试中常用的英语对话

    Q: Can you sell yourself in two minutes? Go for it. (你能在两分钟內自我推荐吗?大胆试试吧!) A: With my qualifications ...

  4. jsp/html开发中常用的JS代码和页面特效代码

    1.jsp/html开发中常用的JS代码 1.后退 前进 <input type="button" value="后退" onClick="hi ...

  5. Python/Java程序员面试必备常用问题解析与答案

    转自AI算法联盟,理解python技术问题,以及一些常见的java面试中经常遇到的问题,这些面试问题分为四类: 是什么(what) 如何做(how) 说区别/谈优势(difference) 实践操作( ...

  6. 复习Java小球游戏代码分享Java面试题MySQL中常用的锁生活【记录一个咸鱼大学生三个月的奋进生活】021

    记录一个咸鱼大学生三个月的奋进生活021 复习Java小球游戏 游戏界面的代码 小球运动线程的代码 运行游戏的代码 运行结果 代码分享 学习Java面试题(MySQL中常用的锁) 照片分享 复习Jav ...

  7. java面试手写单链表_(转)面试大总结之一:Java搞定面试中的链表题目

    packageLinkedListSummary; importjava.util.HashMap; importjava.util.Stack; /** * http://blog.csdn.net ...

  8. java range(10)_Java 中的十个 ” 单行代码编程 ” ( OneLiner )

    原标题:Java 中的十个 " 单行代码编程 " ( OneLiner ) 作者:飒然Hang 原文链接:www.rowkey.me/blog/2017/09/09/java-on ...

  9. Discuz中常用的编辑器代码

    首先,后台  帖子管理 discuz 代码 开启 可用 然后,论坛管理  论坛板块  编辑    帖子选项中  允许使用 Discuz! 代码: 选择  是 1.[ b]文字:在文字的位置可以任意加入 ...

最新文章

  1. 基于 CoreText 实现的高性能 UITableView
  2. Openresrt最佳案例 | 第2篇:Lua入门
  3. 新网站做好前期优化为后期长久运营打好基础
  4. STM32F103 CAN中断发送功能的再次讨论
  5. 18_clickhouse副本同步与高可用功能验证,分布式表与集群配置,数据副本与复制表,ZooKeeper整合,创建复制表,副本同步机制,数据原子写入与去重,负载平衡策略,案例(学习笔记)
  6. Docker和容器简介
  7. Android AsyncTask源码解析
  8. Flutter学习 — 使用WebSockets
  9. VS2008下的配置opencv
  10. 《MySQL必知必会》学习笔记——第四章(检索数据)
  11. 数据分析常用的100个指标和术语
  12. macos复制粘贴快捷键 快速_mac os系统复制粘贴ctrl+c ctrl+v快捷键实现方式
  13. java 系统临时目录_在java中创建临时文件夹
  14. 冒险岛 PHP,php基础知识
  15. 复制粘贴之后出现问号怎么办_CAD图形文字复制粘贴以后出现变化该怎么办?
  16. 2020使用html、js、正则表达式做一个前端注册表单信息验证
  17. 高什么发,什么并发,高并什么? ? ?
  18. 第六讲:STM32F4芯片解读
  19. 三星电视显示服务器无响应,三星液晶电视死机怎么办 三星液晶电视死机解决方法...
  20. 计算机专业二级证书有哪些科目,国家计算机二级考试都有哪些科目以及考核形式?...

热门文章

  1. 安装mariadb、安装Apache
  2. 【深度学习系列】用PaddlePaddle和Tensorflow实现经典CNN网络AlexNet
  3. Unable to find required classes (javax.activation.DataHandler and javax.mail.internet.MimeMultipart)
  4. mint mvc文件上传功能——使用篇
  5. Source Code Library 源代码收集器
  6. sql,dateadd,datediff
  7. 熊猫数据集_用熊猫掌握数据聚合
  8. 抽象类细分举行_什么是用聚类技术聚类的客户细分
  9. leetcode 160. 相交链表(双指针)
  10. 您应该在2020年首先学习哪种编程语言? ɐʌɐɾdıɹɔsɐʌɐɾ:ɹǝʍsuɐ