jdk1.8( jdk11代码有所不同) 的Integer包装类中提供了已经封装好的进制转换函数 toBinaryString(), toOctalString(), toHexString(),下面分析一下它们的源码。

//转二进制
public static String toBinaryString(int i) {
return toUnsignedString0(i, 1);
}
//转八进制
public static String toOctalString(int i) {
return toUnsignedString0(i, 3);
}
//转十六进制
public static String toHexString(int i) {
return toUnsignedString0(i, 4);
}
从以上源码可以看出,它们都调用了toUnsignedString0()这个函数,只是第二个参数shift的值不同.

  1. toUnsignedString()
    接下来我们看看 toUnsignedString()是怎么实现的.

private static String toUnsignedString0(int val, int shift) {
// assert shift > 0 && shift <=5 : “Illegal shift value”;
// 第一步: 计算出用于表示二/八/十六进制的字符数组的长度,并创建字符数组.
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
//第二步 使用formatUnsignedInt方法填充字符数组,得到所需进制表示的字符串并返回
formatUnsignedInt(val, shift, buf, 0, chars);
return new String(buf, true);
}
第一步中最关键的字符数组长度的计算是通过numberOfLeadingZeros()方法计算int变量的计算机二进制表示的高位连续0位的数量,进而获得最高非0位到最低位的长度,也就是需要表示的位数, 负数的最高位是1。例如整数18在计算机中的二进制存储为0000,0000,0000,0000,0000,0000,0001,0010,那么需要表示的部分便是1,0010,前面的28位均为0,不用表示。而-18在计算机中的二进制存储为 11111111111111111111111111101110, 都须要表示.

  1. numberOfLeadingZeros()
    这个函数一般给出的解决方案是通过判断高位是否为0,然后移位重复判断,记录连续的0的个数。如下列代码:

public static int numberOfLeadingZeros0(int i){
if(i == 0) {
return 32; // i为0,则32位全为0
}
int n = 0;
int mask = 0x80000000; // 即 1000 0000 0000 0000 0000 0000 0000 0000
int j = i & mask; //取出符号位,如符号位为 1,表示负数,则直接返回n=0.
//从高位向低位搜索连续出现的0的个数
while(j == 0){
n++; // 计数0出现的次数
i <<= 1; // <<表示左移移位,不分正负数,低位补0
j = i & mask; // 取出左移后最高位的值。
}
return n;
}
以上方法的平均时间复杂度是o(k),k为变量i的位数,如果i为int,k就是32。这种很基本的库函数会经过很多次的调用,需要进一步的优化.

java源码中的写法更加优秀:

//求从高位向低,连续出现的0的个数
public static int numberOfLeadingZeros(int i) {
if (i == 0)
return 32;
int n = 1;
// >>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0
if (i >>> 16 == 0) { n += 16; i <<= 16; } // <<表示左移移,不分正负数,低位补0
if (i >>> 24 == 0) { n += 8; i <<= 8; }
if (i >>> 28 == 0) { n += 4; i <<= 4; }
if (i >>> 30 == 0) { n += 2; i <<= 2; }
n -= i >>> 31;
return n;
}

这个地方采用的是分治法. (可以想想二分搜索的过程)
意思就是通过划分16,8,4,2等逐步缩小的四个区间进行判断和移位。不管if的代码块是否执行,需要判断的位的区间也在随着代码的推进而逐步缩减。它通过分层次的缩小需要求解的域的范围,减少计算次数.

分析过程如下:

if( i>>>30==0 )判断完后还有两位,为什么最后 n-= i>>>31, 这里i 右移了31位,等于只留下了最高位,那么第二高位怎么没有处理呢?

因为,在方法的开始已经判断并返回了变量为0即所有的位均为0的情况,所以能进行到后面的代码部分,说明变量不为0,即必然存在至少一个位为1,而我们在选择需要讨论的位区间时,始终选择的是包含位为1的区间进行讨论。所以,在最后区间长度变为2时,区间中必然至少存在一位是1,而我们讨论的是高位连续的0的个数,如果区间第一位为1,那么第二位自然不用讨论。如果区间第一位是0,那么第二位必然是1,也不需要讨论。所以这里只用讨论第一位的情况

  1. formatUnsignedInt()
    经过上面的以数字18为例的计算后,我们可以得到18的二进制高位会出现连续的 27个0, 接下来我们回到toUnsignedString()函数看程序.

//以18为例
private static String toUnsignedString0(int val, int shift) {

    int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);  // mag=5int chars = Math.max(((mag + (shift - 1)) / shift), 1);    //  chars=5char[] buf = new char[chars];    formatUnsignedInt(val, shift, buf, 0, chars);// Use special constructor which takes over "buf".return new String(buf, true);

}
它声明了buf字符数组,长度为 5. 接着调用 formatUnsignedInt( 18, 1, buf, 0, 5); 下面分析这个函数的源码:

// val=18 shift=1 offset=0 len=5
static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
int charPos = len; // 5
int radix = 1 << shift; // 左移位,将运算数的二进制整体左移指定位数,低位用0补齐
//这样二进制的radix为 10(对应10进制为2) 八进制为 1000 (对应10进制为8) 十六进制为 1 0000(对应10进制为16)
int mask = radix - 1;
// mask为掩码
// 如要转为二进制时mask为1 转为八进制为7 转为十六进制为15
// 用二进制表示分别为 1 111 1111
do {
buf[offset + --charPos] = Integer.digits[val & mask];
// val & mask 相当于一个%(mask+1)运算,它计算出 val对应 mask 这个进制的余数,这个数正好可以对应到 digits中的下标,。 接着通过 Integer.digits取出它的值. 存到 buf数组对应位置
val >>>= shift; //再将 val 右移shift位,相当于 /(shift+1).

        //以上对应的就是十进制转n进制的 模n取余法的算法实现了. } while (val != 0 && charPos > 0);return charPos;

}

以上调用了 Integer.digits 静态数组. 如下:

final static char[] digits = {
‘0’ , ‘1’ , ‘2’ , ‘3’ , ‘4’ , ‘5’ ,
‘6’ , ‘7’ , ‘8’ , ‘9’ , ‘a’ , ‘b’ ,
‘c’ , ‘d’ , ‘e’ , ‘f’ , ‘g’ , ‘h’ ,
‘i’ , ‘j’ , ‘k’ , ‘l’ , ‘m’ , ‘n’ ,
‘o’ , ‘p’ , ‘q’ , ‘r’ , ‘s’ , ‘t’ ,
‘u’ , ‘v’ , ‘w’ , ‘x’ , ‘y’ , ‘z’
};

Integer类的toBinaryString源码分析相关推荐

  1. StringBuffer类【JDK源码分析】

    StringBuffer类[JDK源码分析] 前言 推荐 说明 StringBuffer类 基本信息 属性 构造方法 部分方法 length capacity append insert revers ...

  2. 通用Mapper Example类使用以及源码分析

    目录 一.通用Mapper Example类使用 1. 常用使用方式举例 2. 使用方式:条件嵌套组合 二.通用Mapper Example类源码分析 1. 代码细节理解 1.1 Criteria类 ...

  3. JDK源码分析 FutureTask源码分析

    文章目录 前言 一.Callable接口 二.Future接口 三.FutureTask源码分析 3.1 Future继承结构图 3.2 参数介绍 3.3 构造函数 3.4. FutureTask的A ...

  4. Android 别踩白块 源码分析

    文章目录 一.项目下载 二.项目分析 三.源码分析 1.实体层(Entity) (1).Block类 [1].属性 [2].方法 (2).SuccGroup类 [1].属性 [2].方法 (3).Fa ...

  5. Android MultiDex 源码分析

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.启用 MultiDex Android 5.0 和之后的版本 Android 5.0 之前的版本 二.MultiD ...

  6. JDK源码分析-Integer

    Integer是平时开发中最常用的类之一,但是如果没有研究过源码很多特性和坑可能就不知道,下面深入源码来分析一下Integer的设计和实现. Integer: 继承结构: -java.lang.Obj ...

  7. 深入OKHttp源码分析(二)----OkHttp任务调度核心类Dispatcher解析

    OkHttp任务调度核心类Dispatcher解析 上一篇我们分析了okhttp的同步和异步请求的执行流程并进行了源码分析,深入OKHttp源码分析(一)----同步和异步请求流程和源码分析 那么今天 ...

  8. 这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析

    前言 package com.jvm.classloader;class Father2{public static String strFather="HelloJVM_Father&qu ...

  9. java parseint 负号_java.lang.Integer#parseInt() 源码分析

    Integer#parseInt() 是我们经常使用的一个函数, 是 Integer 类提供的一个静态工具方法, 其作用就是将字符串的数字转换为 int 类型. 一个更通用的 parseInt() 的 ...

最新文章

  1. jquery EasyUI导入js顺序
  2. java reflectionutils_ReflectionUtils工具类-装载
  3. 【Android 应用开发】分析各种Android设备屏幕分辨率与适配 - 使用大量真实安卓设备采集真实数据统计
  4. java调用net webservice_java调用.net的webservice
  5. js代码转python_Python和JavaScript间代码转换的4个工具
  6. python学习-递归(阶乘、汉诺塔)
  7. 想不到,那些让我半夜偷偷收藏的沙雕表情包,竟是出自AI之手
  8. 51 NOD 1363 最小公倍数之和 (欧拉函数思维应用)
  9. 【覆盖安装】通用测试点
  10. MySQL5添加外键约束错误 (Error Code : 1005)
  11. Docker组队学习(一)
  12. 进阶 09 Map集合
  13. EF中DataContext创建的两段代码收藏
  14. 百度AI实现图片转文字-python
  15. C语言飞机大战程序思路,C语言代码实现飞机大战
  16. Application.platform 平台
  17. matlab db dbm dbfs,dbfs(dbfs和dbm的换算)
  18. 有符号拓展:signed-extending无符号拓展:unsigned-extending
  19. 计算机网络信息中心研究生,计算机网络信息中心研究生招生常见问题答疑
  20. java版基础排序归并排

热门文章

  1. 如何合并多个(.txt或其他)文件到一个文件
  2. mysql数字连接,MySQL - 已达到数字连接
  3. vb combox获取选定index_Python-新闻评论获取
  4. 字符串匹配之KMP算法详解
  5. note_maven中的常用命令
  6. idea中tomcat服务器的配置
  7. Java中使用JNA实现全局监听Windows键盘事件
  8. html快捷保存图片,如何使用360浏览器快速保存图片
  9. 【NLP】毕设学习笔记(八)“前馈 + 反馈” = 循环神经网络RNN
  10. mysql markdown_mysql+数据库学习笔记(markdown)