Java必备知识之位运算及常见进制解读
作者:吾日三省贾斯汀 发布时间:2022-02-10 00:42:19
您好,我是贾斯汀,欢迎又进来学习啦!
【学习背景】
学习Java的小伙伴,都知道想要提升个人技术水平,阅读JDK源码少不了,但是说实话还是有些难度的,底层源码实现的原理离不开各种常用的数据结构和算法,很多时候还会用到各种位运算,比如面试必问和工作写烂透了的HashMap,就一个put(key,value)添加元素的底层实现,就用到了各种位运算知识,不对位运算略知一二,你还真读不懂它的源码,所以本文主要对Java中的几种位运算以及常见进制的说明,还会以HashMap底层实现添加元素四部曲
展开说明,希望能提高提升自己对源码的理解,也希望能帮助到有需要的小伙伴~
进入正文~
常见几种进制?
二进制(Binary)
数值范围0,1,满2进1
以0b或0B开头
bit比特是计算机最小存储单元,1个bit占用1个二进制位即0或1
1个byte字节有8个bit即占用8个二进制位
int整型4字节占用32个二进制位
二进制左半部分表示高位,右半部分为低位
二进制最高位为0表示正数,最高位为1表示负数
二进制原码取反得到反码,反码补1得到补码,负数使用补码表示
八进制(Octal)
数值范围0-7,满8进1
以数字0开头表示
十进制(Decimal)
数值范围0-9 ,满10进1
日常阿拉伯数字即十进制
十六进制(Hexadecimal)
数值范围0-9及A-F,满16进1
以0x或0X开头表示。 此处的A-F不区分大小写
Java八种按位运算?
按位与(&)
都为1则得1
按位或(|)
有一个为1即得1
按位异或(^)
不同得1,相同得0
按位取反(~)
取反即1变0、0变1
按位左移(<<)
按位左移几位,高位会被截掉几位,正负数,低位都会被补几个0
按位右移(>>)
按位右移几位,低位就会被截掉几位,正数高位会被补几个0,负数高位会被补几个1
按位无符号右移(>>>)
按位右移几位,低位就会被截掉几位,正负数数高位会被补几个0
按位无符号左移(<<<)
按位左移几位,高位就会被截掉几位,正负数数低位都会被补几个0
HashMap添加元素四步曲用到的位运算?
前奏:HashMap如何添加一个元素?
HashMap底层数据结构是数组+链表
,通过put(K key, V value)
方法添加元素,底层四步曲如下:
第一步曲:根据key得到hashCode值
第二步曲:根据hashCode值计算出hash值
第三步曲:根据hash值计算出元素(key/value)最终要放在哪个数组index下标
第四步曲:最后根据元素(key/value)新建节点并保存到指定数组index下标位置
Java HashMap添加元素的示例代码:
HashMap<Object, Object> map = new HashMap<>();
map.put("name","Justin");
HashMap底层put(key,value)方法源码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
接下来将解读底层源码用到哪些位运算,有什么奥妙之处
第一步曲
根据key得到hashCode值
可以看到hash值计算的过程就用到了^
(异或)和>>>
(无符号右移)两种位运算
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这里key是字符串"name",String重写了计算字符串hashCode值的hashCode()方法,源码如下:
计算得到hashCode值为3373707
第二步曲
根据hashCode值计算出hash值(h = key.hashCode()) ^ (h >>> 16)
即 (3373707) ^ (3373707 >>> 16)
3373707
二进制表达0000000001100110111101010001011
h >>> 16
二进制表达00000000000000000000000000110011
根据^
异或运算原理即不同得1,相同得0
得到3373707 ^ (3373707 >>>16)
二进制结果为:0000000001100110111101010111000
进制在线转换:http://tools.jb51.net/transcoding/hexconvert
即计算key的hash值得到3373752
,断点往后查看hash值刚好也是这个值
第三步曲
根据hash值计算出元素(key/value)最终要放在哪个数组index下标
公式:i = (n - 1) & hash
这里就用到了&
按位与运算(都为1则得1)
公式(n - 1) & hash
的奥妙之处在于,n
表示HashMap中的数组容量大小,并且刚好是16,32,64…2的次方,这种情况其实是等效于 hash % n
取模,计算出的数组index下标值一样,还能够保证不会数组下标越界
但是HashMap这里没有使用%
取模,因为hash值是int整型即十进制数值,使用%取模会先将内存数据转成十进制再进行运算,多了这部分的性能开销,因此效率比较低
HashTable底层倒是用的%取模,hash值与十六进制0x7FFFFFFF
做按位与运算目的是为了保证hash值始终是正数
有的小伙伴可能会问了,使用%取模计算,那这里为啥HashTable还在用,我想说的是其实也可以优化,只不过HashTable本身就是主打synchronized线程安全,也就不考虑优化%取模为位运算了
第四步曲
最后根据元素(key/value)新建节点并保存到指定数组index下标位置
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
终曲:为什么HashMap底层源码用这么多位运算?
关于位运算的使用,文中在介绍第三步曲时,也提到了HashMap计算数组下标使用%取模和位运算的问题,使用于位运算的奥妙之处在直接从内存读取数据进行计算,不需要转成十进制,如果使用%取模需要先转成十进制,有性能开销,效率比较低
HashMap底层除了文中提到的^
按位异或、>>>
无符号右移、&
按位与位运算,其实在HashMap的扩容机制resize()
中,还用到了<<
左移运算oldCap << 1
这里oldCap << 1
刚好是两倍,可以总结的说一个数与n
进行左移运算,结果为这个数乘以2的n次方oldCap << 1
等值 oldCap = oldCap * (2的n次方)
同理,一个数与n
进行右移运算结果为这个数除以2的n次方oldCap >> 1
等值 oldCap = oldCap / (2的n次方)
**
来源:https://blog.csdn.net/JustinQin/article/details/120505776


猜你喜欢
- /*最小树形图图模版-朱刘算法模版说明:点标号必须0-(N-1) 必须去除到自身的点(到自身的边的边权赋无限大)*/
- strftime函数主要用于时间格式化,它的函数原型如下:size_t __cdecl strftime(char * __restrict
- 二维码是越来越流行了,很多地方都有可能是使用到。如果是静态的二维码还是比较好处理的,通过在线工具就可以直接生成一张二维码图片,比如:草料二维
- 本文实例为大家分享了Android实现系统日历同步日程的具体代码,供大家参考,具体内容如下1、权限<uses-permission a
- activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业务非常的复杂,比如一个简单的采购流程:流程如下: 供应商上新商品
- String[, ,] items = new String[,,] { { { "A1", "A2"
- 新公司工程是用Maven管理的,技术上使用了JPA,但是我导入工程到MyEclipse时,applicationContext.xml中提示
- 在我们应用程序的业务逻辑中,经常会碰到参数校验的情况,手动的在代码层上面进行校验就会带来很不好的体验,阅读、维护的成本会大大增加,造成冗余。
- 本文记录刚接触Android开发搭建环境后新建工程各种可能的报错,并亲身经历漫长的解决过程(╥╯^╰╥),寻找各种偏方,避免大家采坑,希望能
- 本文实例讲述了Android编程实现实时监听EditText文本输入的方法。分享给大家供大家参考,具体如下:平时在做Android开发过程中
- 1.根据入参带分页参数进行sql查询分页 Criteria criteria = n
- 摘要:其实两种方法归结起来看还是一种,都是利用Thread的构造器进行创建,区别就是一种是无参的,一种是有参的。一、继承Thread线程类:
- public class ReadBitmap { public void readByte(Context c, String name,
- 本文较为详细的讲述了Android下Activity全屏显示实现方法。分享给大家供大家参考。具体方法如下:方法一:使用xml的方法,在该项目
- 效果图简单的效果图(使用开源库)[FlowLayout](“ https://github.com/hongyangAndroid/Flow
- 感觉很久不写模拟器代码了,昨天调试的时候碰了点壁,记录下来,避免大家再跟我犯同样的错误。加入Javascript脚本的地方:HtmlElem
- VisualVM是JDK自带的一款全能型性能监控和故障分析工具,包括对CPU使用、JVM堆内存消耗、线程、类加载的实时监控,内存dump文件
- 上移动端的测试课,老师和同学们用的都是eclipse, 只有我一个人用的是idea(用了两款软件之后觉得IDEA更好),真的太难了,配置
- 背景在写一个东西滑动删除列表的时候,出现了一个问题。我的需求是,左滑然后出现delete,然后点击delete,让该滑块消失。我在点列表的第
- 1.<constant name="struts.i18n.encoding" value="UTF-8