Integer和Long部分源码分析
Integer和Long的java中使用特别广泛,本人主要一下Integer.toString(int i)和Long.toString(long i)方法,其他方法都比较容易理解。
Integer.toString(int i)和Long.toString(long i),以Integer.toString(int i)为例,先看源码:
- /**
- * Returns a {@code String} object representing the
- * specified integer. The argument is converted to signed decimal
- * representation and returned as a string, exactly as if the
- * argument and radix 10 were given as arguments to the {@link
- * #toString(int, int)} method.
- *
- * @param i an integer to be converted.
- * @return a string representation of the argument in base 10.
- */
- public static String toString(int i) {
- if (i == Integer.MIN_VALUE)
- return "-2147483648";
- int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
- char[] buf = new char[size];
- getChars(i, size, buf);
- return new String(buf, true);
- }
通过调用stringSize来计算i的长度,也就是位数,用来分配合适大小的字符数组buf,然后调用getChars来设置buf的值。
stringSize的Integer和Long中的实现有所不同,先看看源码
Integer.stringSize(int x)源码:
- final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
- 99999999, 999999999, Integer.MAX_VALUE };
- // Requires positive x
- static int stringSize(int x) {
- for (int i=0; ; i++)
- if (x <= sizeTable[i])
- return i+1;
- }
将数据存放在数组中,数组中的下标+1就是i的长度,当x小于sizeTable中的某个值时,这样设计只需要循环就可以得出长度,效率高。
Long.stringSize(long x)源码:
- // Requires positive x
- static int stringSize(long x) {
- long p = 10;
- for (int i=1; i<19; i++) {
- if (x < p)
- return i;
- p = 10*p;
- }
- return 19;
- }
因为Long的十进制最大长度是19,在计算长度时通过反复乘以10的方式求出来的,可能会问为什么不用Integer.stringSize(int x)的方法,我也没有找到合适的解释。
传统的方案可能是通过反复除以10的方法求出来的,但是这样的效率低,因为计算机在处理乘法时要比除法快。
getChars(int i, int index, char[] buf)源码:
- /**
- * Places characters representing the integer i into the
- * character array buf. The characters are placed into
- * the buffer backwards starting with the least significant
- * digit at the specified index (exclusive), and working
- * backwards from there.
- *
- * Will fail if i == Integer.MIN_VALUE
- */
- static void getChars(int i, int index, char[] buf) {
- int q, r;
- int charPos = index;
- char sign = 0;
- if (i < 0) {
- sign = '-';
- i = -i;
- }
- // Generate two digits per iteration
- while (i >= 65536) {
- q = i / 100;
- // really: r = i - (q * 100);
- r = i - ((q << 6) + (q << 5) + (q << 2));
- i = q;
- buf [--charPos] = DigitOnes[r];
- buf [--charPos] = DigitTens[r];
- }
- // Fall thru to fast mode for smaller numbers
- // assert(i <= 65536, i);
- for (;;) {
- q = (i * 52429) >>> (16+3);
- r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
- buf [--charPos] = digits [r];
- i = q;
- if (i == 0) break;
- }
- if (sign != 0) {
- buf [--charPos] = sign;
- }
- }
这是整个转换过程的核心代码,首先确定符号,其次当i>=65536时将i除以100,并且通过DigitOnes[r]和DigitTens[r]来获取十位和个位上的值,因为除法慢,所以一次性除以100提高效率,DigitOnes和DigitTens如下:
- final static char [] DigitTens = {
- '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
- '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
- '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
- '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
- '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
- '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
- '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
- '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
- '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
- '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
- } ;
- final static char [] DigitOnes = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- } ;
假设r=34,通过查表可以得出DigitOnes[r]=4,DigitTens[r]=3。
q = (i * 52429) >>> (16+3); 的本质是将i/10,并去掉小数部分,219=524288,52429/524288=0.10000038146972656,为什么会选择52429/524288呢,看了下面就知道了:
- 2^10=1024, 103/1024=0.1005859375
- 2^11=2048, 205/2048=0.10009765625
- 2^12=4096, 410/4096=0.10009765625
- 2^13=8192, 820/8192=0.10009765625
- 2^14=16384, 1639/16384=0.10003662109375
- 2^15=32768, 3277/32768=0.100006103515625
- 2^16=65536, 6554/65536=0.100006103515625
- 2^17=131072, 13108/131072=0.100006103515625
- 2^18=262144, 26215/262144=0.10000228881835938
- 2^19=524288, 52429/524288=0.10000038146972656
可以看出52429/524288的精度最高,并且在Integer的取值范围内。
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... 用位运算而不用乘法也是为了提高效率。
注:以上分析内容仅个人观点(部分参考网上),如有不正确的地方希望可以相互交流。
Integer和Long部分源码分析的更多相关文章
- Integer面试连环炮以及源码分析
场景: 昨天有位朋友去面试,我问他面试问了哪些问题,其中问了Integer相关的问题,以下就是面试官问的问题,还有一些是我对此做了扩展. 问:两个new Integer 128相等吗? 答:不.因 ...
- Integer面试连环炮以及源码分析(转)
场景: 昨天有位朋友去面试,我问他面试问了哪些问题,其中问了Integer相关的问题,以下就是面试官问的问题,还有一些是我对此做了扩展. 问:两个new Integer 128相等吗? 答:不.因 ...
- JDK源码分析-Integer
Integer是平时开发中最常用的类之一,但是如果没有研究过源码很多特性和坑可能就不知道,下面深入源码来分析一下Integer的设计和实现. Integer: 继承结构: -java.lang.Obj ...
- 设计模式(十二)——享元模式(Integer缓冲池源码分析)
1 展示网站项目需求 小型的外包项目,给客户 A 做一个产品展示网站,客户 A 的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同: 1) 有客户要求以新闻的形式发布 2) 有客户人要 ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
- MyCat源码分析系列之——BufferPool与缓存机制
更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...
随机推荐
- (二)MySQL8.0(ZIP)、SQLyog安装
一.mysql8.0(ZIP)的安装 安装时看了很多的文章,开始选择的是客户端安装后一直安装失败,就选择了zip安装. 注意:该方法仅适用于8.0版本安装,其余版本未测试 1.下载zip压缩包(两个都 ...
- foreach 集合又抛经典异常了,这次一定要刨根问底
一:背景 1. 讲故事 最近同事在写一段业务逻辑的时候,程序跑起来总是报:集合已修改:可能无法执行枚举操作,硬是没有找到什么情况下会导致这个异常产生,就让我来找一下bug,其实这个异常在座的每个程序员 ...
- ida 调试android之路
系统: Mac OSX 调试环境:IDA7.0, adb 手机环境:红米手机 android 4.4.4 前提条件: 红米手机root之路:https://www.cnblogs.com/dzqdz ...
- docker镜像瘦身思路
docker镜像瘦身思路 一.简介 docker镜像太大,带来了以下几个问题: 存储开销 这块影响其实不算很大,因为对服务器磁盘来说,15GB的存储空间并不算大,除非用户服务器的磁盘空间很紧张 部署时 ...
- 什么了解suite集合实现
Testsuite继承BaseTestSuite其实内部的东西不是太多--生成suite集合的逻辑主要如下-我这里没有扒源码-因为他最终生成的TestsSuite关联的模块比较多--如果贴源码出来-- ...
- Linux下如何查看硬件信息?
我们在 Linux 下进行开发时,有时也需要知道当前的硬件信息,比如:CPU几核?使用情况?内存大小及使用情况?USB设备是否被识别?等等类似此类问题.下面良许介绍一些常用的硬件查看命令. lshw ...
- JVM对算术运算做了什么??
java可以进行数字的加减乘除,但是JVM的运算步骤是什么样子呢?从一个神奇的式子入手,研究下JVM到底做了什么? 先看下图:
- 解决start.spring.io无法访问的情况
将start.spring.io替换成下列网址 http://start.jetbrains.org.cn/ 或者----> 连接手机热点 因为绝大多数无法访问都是因为网络问题
- ceph bluestore与 filestore 数据存放的区别
一. filestore 对象所在的PG以文件方式放在xfs文件中 1 查看所有的osd硬盘,跟其他linux其他硬盘一样,被挂载一个目录中. [root@hz-storage1 ~]# df -h ...
- P1131 [ZJOI2007]时态同步【树形dp】
时态同步 从叶子到根节点统计修改次数.树形\(dp\)思想. 题目描述 小\(Q\)在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字\(1,2,3-\). ...