Java源码 Integer.bitCount实现过程
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
第一眼看这个代码,完全看不懂。
搜查资料之后才懂:
原来是 先 两个两个一组,求二进制1的个数,并且用两位二进制存储在原处,然后四个四个一组,求二进制位1的个数,再把它存储以4位二进制到原处。以此类推直到计算完成。不得不感叹这个算法设计的精妙之处。
Google了一番,没有查到为什么这个设计。知其然,不知其所以然。
于是,我决定模拟一下发明者的想法,为什么要这么算,怎么想到的,进行求解过程的分析:
想到面试题 5升和3升的杯子 取 4升水这种问题了,这种题不就是利用加减法来回计算吗?利用已知的来求解未知的。
我们平时数数是不是喜欢一对一对的数啊?
先观察存储的情况:
| src | store | remark |
|---|---|---|
| 00 | 00 | 这两位没有,那就用0存储 |
| 01 | 01 | 这两位只有一个1,就用1存储 |
| 10 | 01 | 这两位也只有一个1,也用1存储 |
| 11 | 10 | 这两位有两个1,用10存储 |
那么就一对一对的数,已知 src列 求出 store列?
列式计算:
- 设 λ = i - x
- 00 = 00 - 00;
- 01 = 01 - 00;
- 01 = 10 - 01;
- 10 = 11 - 01;
那么 x 又如何通过i得到呢?
我们手无寸铁,对CPU来说也只有加法和移位的手段。假如发明者列出这种算式,敏感的他一下子
很容易看出来:
x=i>>>1
就这么简单
那么得到:
λ = i - (i>>>1)
那么i不止两位怎么处理?如果这个是最后的两位,那么移位之后后面一位二进制可以抹掉
而前面的移位会影响后面的最高位,那么把移出去的那一位消除:
i>>>1 & 01;
即为:01010101 01010101 01010101 01010101
λ = i - (i>>>1 & 0x55555555)
问题解决。
那么 计算了两位的如何计算4位的二进制位呢?
枚举第一步计算完成的所有的情况:
| src | target | remark | ref |
|---|---|---|---|
| 0000 | 0000 | = 0000 & 0011 | |
| 0001 | 0001 | = 0001 & 0011 | 01 = 01 & 11 |
| 0010 | 0010 | = 0010 & 0011 | 10 = 10 & 11 |
| 0100 | 0001 | = 01 + 00 | |
| 0101 | 0010 | = 01 + 01 | |
| 0110 | 0011 | = 01 + 10 | |
| 1000 | 0010 | = 10 + 00 | |
| 1001 | 0011 | = 10 + 01 | |
| 1010 | 0100 | = 10 + 10 |
后面两组可以参照第一组的结果,那么可以推算
四位中低两位 bb = aabb & 0011,主要要计算与高两位的和:
已知可以用1100& aabb =aa00得到左边的值,但是多了两个00,那么要计算aa + bb:
可以 aabb>>>2 = 00aa(bb)只看这两位,移位多出去的被00消除,不影响后面的计算。
即:
λ =( i & 0x0011) + (i>>>2 & 0x0011)
也就是:
λ =( i & 0x33333333) + (i>>>2 & 0x33333333)
同理求8位里面的两边4位之和:
λ =( i + i>>>4) & 0x0F0F0F0F
求16位的两边之和:
λ = i + (i >>> 8);
由于二等分是8位,而8位一共有4份。
A B C D
(C>>>8) + D D处8位的结果最大为 0001 0000不会进位到C。
(B>>>8) + C C处8位的结果最大为 0001 0000不会进位到B。
(A>>>8) + B B处8位的结果最大为 0001 0000不会进位到B。
A + 0 A处最大结果为 0000 1000
得到
A A+B B+C C+D
最后是求32位全部的内容也就是求(A+B)+(C+D)
A A+B B+C C+D
+
0 0 A A+B
也就是
λ= i + (i >>> 16)
A A+B A+B+C A+B+C+D
A+B+C+D最大也就32个:
0000 0000 0000 0000 0000 0000 0010 0000
0000 0000 0000 0000 0000 0000 0011 1111 = 0x3F
之所以要return i&0x3F,就是把前面抹干净。
Java源码 Integer.bitCount实现过程的更多相关文章
- Java源码——Integer
最近在研究java的源代码,但是由于自己英语水平有限,所以想使用中文注释的方式把源码里的方法全部重写 一遍,下面是楼主整理出来的一小部分.我把整体的项目托管到GitHub上了,欢迎大家前去交流学习. ...
- JDK源码 Integer.bitCount(i)
1.问题:输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 2.解决方法很多,JDK提供了一种,如下图 /** * Returns the number of one-bits in ...
- Java源码解释之Integer.bitCount
Java中的Integer.bitCount(i)的返回值是i的二进制表示中1的个数.源码如下: public static int bitCount(int i) { // HD, Figure 5 ...
- Java源码解读(一)——HashMap
HashMap作为常用的一种数据结构,阅读源码去了解其底层的实现是十分有必要的.在这里也分享自己阅读源码遇到的困难以及自己的思考. HashMap的源码介绍已经有许许多多的博客,这里只记录了一些我看源 ...
- java 源码编译
Java语言的“编译期”其实是一段“不确定”的操作过程,因为它可能是指一个前端编译器(叫“编译器的前段”更准确)——把*.java文件转变成*.class文件的过程:也可能是虚拟机的后端运行期编译器( ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- 从Java源码到Java字节码
Java最主流的源码编译器,javac,基本上不对代码做优化,只会做少量由Java语言规范要求或推荐的优化:也不做任何混淆,包括名字混淆或控制流混淆这些都不做.这使得javac生成的代码能很好的维持与 ...
- java源码——0~9十个数字不重复地使用使加法表达式成立
这个问题是在我写个的几个博客里较为复杂的一个.首先,先看看整个问题的表述. 星号表示0~9的一个数字,而且不允许重复,使得下面的加法表达式成立.输出所有结果. ※ ※ ※ ※ ※ + 2 ...
- 如何阅读Java源码 阅读java的真实体会
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比 ...
随机推荐
- 品Spring:bean定义上梁山
认真阅读,收获满满,向智慧又迈进一步... 技术不枯燥,先来点闲聊 先说点好事高兴一下.前段时间看新闻说,我国正式的空间站建设已在进行当中.下半年,长征五号B运载火箭将在海南文昌航天发射场择机将空间站 ...
- ELK搭建实时日志分析平台
ELK搭建实时日志分析平台 导言 ELK由ElasticSearch.Logstash和Kiabana三个开源工具组成,ELK平台可以同时实现日志收集.日志搜索和日志分析的功能.对于生产环境中海量日志 ...
- 对象实例Vue
var vm = new Vue({ el:'#app', data:{}, //数据 methods:{}, //方法调用 filters:{}, //私有过滤器 directives:{}, // ...
- Jib构建镜像的问题分析(Could not find or load main class ${start-class})
问题简述 通过Jib插件将SpringBoot工程制作成Docker镜像成功,但是运行镜像的时候报错(Could not find or load main class ${start-class}) ...
- Python 编译器与解释器
Python 编译器与解释器 Python的环境我们已经搭建好了,可以开始学习基础知识了.但是,在此之前,还要先说说编译器与解释器相关的内容. 如果这部分内容,让你觉得难以理解或不能完全明白,可以暂时 ...
- linux服务器创建虚拟路径解决文件上传路径隔离问题
需求环境 图片上传最简单的就是上传web项目下,这样图片与项目不可分离会产生很多不必要的影响.例如:重新部署项目需要把所有上传的图片再copy一份等. 图片与项目分离有好几种方式: 方式一.在linu ...
- Spring Boot认证:整合Jwt
背景 Jwt全称是:json web token.它将用户信息加密到token里,服务器不保存任何用户信息.服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证. 优点 简洁: 可以通过 ...
- 【原创】(六)Linux内存管理 - zoned page frame allocator - 1
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Go语言入门教程(十一)
原创: IT干货栈 Hello,各位小伙伴大家好,我是小栈君,昨天讲了关于go语言的函数的定义和自定函数的部分种类,我们今天接着上期所讲的内容继续进行分享. 一.函数之无参有返回值 有返回值的函数,必 ...
- reduce方法应用技巧
定义和用法 reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值. 注意: reduce() 对于空数组是不会执行回调函数的. 浏览器支持 方法 Chro ...