java Integer 源码学习
轉(zhuǎn)載自http://www.hollischuang.com/archives/1058
Integer 類在對(duì)象中包裝了一個(gè)基本類型 int 的值。Integer 類型的對(duì)象包含一個(gè) int 類型的字段。
此外,該類提供了多個(gè)方法,能在 int 類型和 String 類型之間互相轉(zhuǎn)換,還提供了處理 int 類型時(shí)非常有用的其他一些常量和方法。
類定義
public final class Integer extends Number implements Comparable<Integer>
從類定義中我們可以知道以下幾點(diǎn):
1、Integer類不能被繼承
2、Integer類實(shí)現(xiàn)了Comparable接口,所以可以用compareTo進(jìn)行比較并且Integer對(duì)象只能和Integer類型的對(duì)象進(jìn)行比較,不能和其他類型比較(至少調(diào)用compareTo方法無(wú)法比較)。
3、Integer繼承了Number類,所以該類可以調(diào)用longValue、floatValue、doubleValue等系列方法返回對(duì)應(yīng)的類型的值。
屬性
一、私有屬性
Integer類中定義了以下幾個(gè)私有屬性:
private final int value; private static final long serialVersionUID = 1360826667806852920L;?
serialVersionUID和序列化有關(guān)。String的源碼學(xué)習(xí)中有介紹,這里不再贅述。
還有一個(gè)私有屬性——value屬性就是Integer對(duì)象中真正保存int值的。
當(dāng)我們使用new Integer(10)創(chuàng)建一個(gè)Integer對(duì)象的時(shí)候,就會(huì)用以下形式給value賦值。還有其他的構(gòu)造函數(shù)在后面會(huì)講。
public Integer(int value) {this.value = value; }?
這里我們討論一下Interger對(duì)象的可變性。從value的定義形式中可以看出value被定義成final類型。也就說(shuō)明,一旦一個(gè)Integer對(duì)象被初始化之后,就無(wú)法再改變value的值。那么這里就深入討論一下以下代碼的邏輯:
/*** Created by hollis on 16/1/22.*/ public class IntegerTest {public static void main(String[] args) {Integer i = new Integer(10);i = 5;} }?
在以上代碼中,首先調(diào)用構(gòu)造函數(shù)new一個(gè)Integer對(duì)象,給私有屬性value賦值,這時(shí)value=10,接下來(lái)使用i=5的形式試圖改變i的值。有一點(diǎn)開(kāi)發(fā)經(jīng)驗(yàn)的同學(xué)都知道,這個(gè)時(shí)候如果使用變量i,那么它的值一定是5,那么i=5這個(gè)賦值操作到底做了什么呢?到底是如何改變i的值的呢?是改變了原有對(duì)象i中value的值還是重新創(chuàng)建了一個(gè)新的Integer對(duì)象呢?
我們將上面的代碼進(jìn)行反編譯,反編譯之后的代碼如下:
public class IntegerTest {public IntegerTest(){}public static void main(String args[]){Integer i = new Integer(10);i = Integer.valueOf(5);} }?
通過(guò)看反編譯之后的代碼我們發(fā)現(xiàn),編譯器會(huì)把i=5轉(zhuǎn)成i = Integer.valueOf(5);這里先直接給出結(jié)論,i=5操作并沒(méi)有改變使用Integer i = new Integer(10);創(chuàng)建出來(lái)的i中的value屬性的值。要么是直接返回一個(gè)已有對(duì)象,要么新建一個(gè)對(duì)象。這里的具體實(shí)現(xiàn)細(xì)節(jié)在后面講解valueOf方法的時(shí)候給出。
二、公共屬性
//值為 (-(2的31次方)) 的常量,它表示 int 類型能夠表示的最小值。 public static final int MIN_VALUE = 0x80000000; //值為 ((2的31次方)-1) 的常量,它表示 int 類型能夠表示的最大值。 public static final int MAX_VALUE = 0x7fffffff; //表示基本類型 int 的 Class 實(shí)例。 public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int"); //用來(lái)以二進(jìn)制補(bǔ)碼形式表示 int 值的比特位數(shù)。 public static final int SIZE = 32; //用來(lái)以二進(jìn)制補(bǔ)碼形式表示 int 值的字節(jié)數(shù)。1.8以后才有 public static final int BYTES = SIZE / Byte.SIZE;?
以上屬性可直接使用,因?yàn)樗麄円呀?jīng)定義成publis static fianl能用的時(shí)候盡量使用他們,這樣不僅能使代碼有很好的可讀性,也能提高性能節(jié)省資源。
方法
構(gòu)造方法
Integer提供了兩個(gè)構(gòu)造方法:
//構(gòu)造一個(gè)新分配的 Integer 對(duì)象,它表示指定的 int 值。 public Integer(int value) {this.value = value; }//構(gòu)造一個(gè)新分配的 Integer 對(duì)象,它表示 String 參數(shù)所指示的 int 值。 public Integer(String s) throws NumberFormatException {this.value = parseInt(s, 10); }?
從構(gòu)造方法中我們可以知道,初始化一個(gè)Integer對(duì)象的時(shí)候只能創(chuàng)建一個(gè)十進(jìn)制的整數(shù)。
Integer valueOf(int i)方法
前面說(shuō)到Integer中私有屬性value的時(shí)候提到
Integer i = new Integer(10); i = 5;?
其中i=5操作時(shí),編譯器會(huì)轉(zhuǎn)成i = Integer.valueOf(5);執(zhí)行。那么這里就解釋一下valueOf(int i)方法是如何給變量賦值的。
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i); }?
以上是valueOf方法的實(shí)現(xiàn)細(xì)節(jié)。通常情況下,IntegerCache.low=-128,IntegerCache.high=127(除非顯示聲明java.lang.Integer.IntegerCache.high的值),Integer中有一段動(dòng)態(tài)代碼塊,該部分內(nèi)容會(huì)在Integer類被加載的時(shí)候就執(zhí)行。
static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}?
也就是說(shuō),當(dāng)Integer被加載時(shí),就新建了-128到127的所有數(shù)字并存放在Integer數(shù)組cache中。
再回到valueOf代碼,可以得出結(jié)論。當(dāng)調(diào)用valueOf方法(包括后面會(huì)提到的重載的參數(shù)類型包含String的valueOf方法)時(shí),如果參數(shù)的值在-127到128之間,則直接從緩存中返回一個(gè)已經(jīng)存在的對(duì)象。如果參數(shù)的值不在這個(gè)范圍內(nèi),則new一個(gè)Integer對(duì)象返回。
所以,當(dāng)把一個(gè)int變量轉(zhuǎn)成Integer的時(shí)候(或者新建一個(gè)Integer的時(shí)候),建議使用valueOf方法來(lái)代替構(gòu)造函數(shù)。或者直接使用Integer i = 100;編譯器會(huì)轉(zhuǎn)成Integer s = Integer.valueOf(10000);
String轉(zhuǎn)成Integer(int)的方法
| 1 2 3 4 5 6 7 8 9 10 | Integer getInteger(String nm) Integer getInteger(String nm, int val) Integer getInteger(String nm, Integer val) Integer decode(String nm) Integer valueOf(String s) Integer valueOf(String s, int radix) int parseUnsignedInt(String s) int parseUnsignedInt(String s, int radix) int parseInt(String s) int parseInt(String s, int radix) |
以上所有方法都能實(shí)現(xiàn)將String類型的值轉(zhuǎn)成Integer(int)類型(如果 String 不包含可解析整數(shù)將拋出NumberFormatException)
可以說(shuō),所有將String轉(zhuǎn)成Integer的方法都是基于parseInt方法實(shí)現(xiàn)的。簡(jiǎn)單看一下以上部分方法的調(diào)用棧。
getInteger(String nm) ---> getInteger(nm, null);--->Integer.decode()--->Integer.valueOf()--->parseInt()?
getInteger
確定具有指定名稱的系統(tǒng)屬性的整數(shù)值。 第一個(gè)參數(shù)被視為系統(tǒng)屬性的名稱。通過(guò)System.getProperty(java.lang.String)?方法可以訪問(wèn)系統(tǒng)屬性。然后,將該屬性的字符串值解釋為一個(gè)整數(shù)值,并返回表示該值的 Integer 對(duì)象。使用?getProperty?的定義可以找到可能出現(xiàn)的數(shù)字格式的詳細(xì)信息。其中參數(shù)nm應(yīng)該在System的props中可以找到。這個(gè)方法在日常編碼中很好是用到。在代碼中可以用以下形式使用該方法:
Properties props = System.getProperties(); props.put("hollis.integer.test.key","10000"); Integer i = Integer.getInteger("hollis.integer.test.key"); System.out.println(i); //輸出 10000?
另外兩個(gè)方法
getInteger(String nm,int val) getInteger(String nm, Integer val)?
第二個(gè)參數(shù)是默認(rèn)值。如果未具有指定名稱的屬性,或者屬性的數(shù)字格式不正確,或者指定名稱為空或 null,則返回默認(rèn)值。
getInteger的具體實(shí)現(xiàn)細(xì)節(jié)如下:
public static Integer getInteger(String nm, Integer val) {String v = null;try {v = System.getProperty(nm);} catch (IllegalArgumentException | NullPointerException e) {}if (v != null) {try {return Integer.decode(v);} catch (NumberFormatException e) {}}return val;}?
先按照nm作為key從系統(tǒng)配置中取出值,然后調(diào)用Integer.decode方法將其轉(zhuǎn)換成整數(shù)并返回。
decode
public static Integer decode(String nm) throws NumberFormatException?
該方法的作用是將 String 解碼為 Integer。接受十進(jìn)制、十六進(jìn)制和八進(jìn)制數(shù)字。
根據(jù)要解碼的 String(mn)的形式轉(zhuǎn)成不同進(jìn)制的數(shù)字。 mn由三部分組成:符號(hào)、基數(shù)說(shuō)明符和字符序列。?—0X123中-是符號(hào)位,0X是基數(shù)說(shuō)明符(0表示八進(jìn)制,0x,0X,#表示十六進(jìn)制,什么都不寫(xiě)則表示十進(jìn)制),123是數(shù)字字符序列。
使用例子舉例如下:
Integer DecimalI = Integer.decode("+10"); Integer OctI = Integer.decode("-010"); Integer HexI = Integer.decode("-0x10"); Integer HexI1 = Integer.decode("#10"); System.out.println(DecimalI); System.out.println(OctI); System.out.println(HexI); System.out.println(HexI1); //10 -8 -16 16?
decode方法的具體實(shí)現(xiàn)也比較簡(jiǎn)單,首先就是判斷String類型的參數(shù)mn是否以(+/—)符號(hào)開(kāi)頭。然后再依次判斷是否以”0x”、“#”、“0”開(kāi)頭,確定基數(shù)說(shuō)明符的值。然后將字符串mn進(jìn)行截取,只保留其中純數(shù)字部分。在用截取后的純數(shù)字和基數(shù)調(diào)用valueOf(String s, int radix)方法并返回其值。
valueOf
public static Integer valueOf(String s) throws NumberFormatException public static int parseInt(String s, int radix) throws NumberFormatException?
返回一個(gè) Integer 對(duì)象。如果指定第二個(gè)參數(shù)radix,將第一個(gè)參數(shù)解釋為用第二個(gè)參數(shù)指定的基數(shù)表示的有符號(hào)整數(shù)。如果沒(méi)指定則按照十進(jìn)制進(jìn)行處理。
該方法實(shí)現(xiàn)非常簡(jiǎn)單:
public static Integer valueOf(String s) throws NumberFormatException {return Integer.valueOf(parseInt(s, 10)); }public static Integer valueOf(String s, int radix) throws NumberFormatException {return Integer.valueOf(parseInt(s,radix)); }?
主要用到了兩個(gè)方法,parseInt(String s, int radix)和valueOf(int i)方法。前面已經(jīng)講過(guò)valueOf方法會(huì)檢查參數(shù)內(nèi)容是否在-127到128之間,如果是則直接返回。否則才會(huì)新建一個(gè)對(duì)象。
parseInt
public static int parseInt(String s) throws NumberFormatException {return parseInt(s,10); }public static int parseInt(String s, int radix) throws NumberFormatException?
使用第二個(gè)參數(shù)指定的基數(shù)(如果沒(méi)指定,則按照十進(jìn)制處理),將字符串參數(shù)解析為有符號(hào)的整數(shù)。除了第一個(gè)字符可以是用來(lái)表示負(fù)值的 ASCII 減號(hào) ‘-‘ (‘\u002D’)外,字符串中的字符必須都是指定基數(shù)的數(shù)字(通過(guò) Character.digit(char, int) 是否返回一個(gè)負(fù)值確定)。返回得到的整數(shù)值。
如果發(fā)生以下任意一種情況,則拋出一個(gè)?NumberFormatException?類型的異常:
第一個(gè)參數(shù)為 null 或一個(gè)長(zhǎng)度為零的字符串。
基數(shù)小于 Character.MIN_RADIX 或者大于 Character.MAX_RADIX。
假如字符串的長(zhǎng)度超過(guò) 1,那么除了第一個(gè)字符可以是減號(hào) ‘-‘ (‘u002D’) 外,字符串中存在任意不是由指定基數(shù)的數(shù)字表示的字符.
字符串表示的值不是 int 類型的值。
示例:
parseInt("0", 10) 返回 0 parseInt("473", 10) 返回 473 parseInt("-0", 10) 返回 0 parseInt("-FF", 16) 返回 -255 parseInt("1100110", 2) 返回 102 parseInt("2147483647", 10) 返回 2147483647 parseInt("-2147483648", 10) 返回 -2147483648 parseInt("2147483648", 10) 拋出 NumberFormatException parseInt("99", 8) 拋出 NumberFormatException parseInt("Hollis", 10) 拋出 NumberFormatException parseInt("Hollis", 27) 返回 411787?
該方法的具體實(shí)現(xiàn)方式也比較簡(jiǎn)單,主要邏輯代碼(省略部分參數(shù)校驗(yàn))如下:
while (i < len) { // Accumulating negatively avoids surprises near MAX_VALUE digit = Character.digit(s.charAt(i++),radix); if (digit < 0) {throw NumberFormatException.forInputString(s); } if (result < multmin) {throw NumberFormatException.forInputString(s); } result *= radix; if (result < limit + digit) { throw NumberFormatException.forInputString(s); } result -= digit; }?
主要思想其實(shí)也很好理解。
“12345”按照十進(jìn)制轉(zhuǎn)成12345的方法其實(shí)就是以下方式:?((1*10)+2)*10)+3)*10+4)*10+5?具體的如何依次取出“12345”中的每一個(gè)字符并將起轉(zhuǎn)成不同進(jìn)制int類型則是Character.digit方法實(shí)現(xiàn)的,這里就不深入講解了。
總結(jié)
上面列舉了很多能夠?qū)tring轉(zhuǎn)成Integer的方法。那么他們之間有哪些區(qū)別,又該如何選擇呢?
parseInt方法返回的是基本類型int
其他的方法返回的是Integer
valueOf(String)方法會(huì)調(diào)用valueOf(int)方法。
如果只需要返回一個(gè)基本類型,而不需要一個(gè)對(duì)象,可以直接使用Integert.parseInt("123");
如果需要一個(gè)對(duì)象,那么建議使用valueOf(),因?yàn)樵摲椒梢越柚彺鎺?lái)的好處。
如果和進(jìn)制有關(guān),那么就是用decode方法。
如果是從系統(tǒng)配置中取值,那么就是用getInteger
int轉(zhuǎn)成String的方法
String toString() static String toString(int i) static String toString(int i, int radix) static String toBinaryString(int i) static String toHexString(int i) static String toOctalString(int i) static String toUnsignedString(int i) static String toUnsignedString(int i, int radix)?
直接看toString方法,toString方法的定義比較簡(jiǎn)單,就是把一個(gè)int類型的數(shù)字轉(zhuǎn)換成字符串類型,但是這個(gè)方法的實(shí)現(xiàn)調(diào)用了一系列方法,通過(guò)閱讀這個(gè)方法,你就會(huì)對(duì)sun公司的程序員產(chǎn)生油然的敬佩。
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); }?
我們把toString方法分解為以下幾個(gè)片段:
片段一:
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);?
片段一
if (i == Integer.MIN_VALUE)return "-2147483648";?
這里先對(duì)i的值做檢驗(yàn),如果等于Int能表示的最小值,則直接返回最小值的字符串形式。那么為什么-2147483648要特殊處理呢?請(qǐng)看代碼片段二的分析。
片段二
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); char[] buf = new char[size];?
這段代碼的主要目的是體取出整數(shù)i的位數(shù),并創(chuàng)建一個(gè)字符數(shù)組。 其中提取I的位數(shù)使用stringSize方法,這個(gè)方法實(shí)現(xiàn)如下:
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; }?
該方法要求傳入一個(gè)正整數(shù),如果傳入的數(shù)字x的值是10000,那么因?yàn)樗笥?,99,999,9999,小于99999.所以他會(huì)返回99999在整型數(shù)組sizeTable中的下標(biāo)”4″+1 = 5。我們看10000這個(gè)數(shù)字的位數(shù)也確實(shí)是5。所以,就實(shí)現(xiàn)了返回一個(gè)正整數(shù)的位數(shù)。
設(shè)置size時(shí),當(dāng)i<0的時(shí)候返回的size數(shù)組在stringSize方法的基礎(chǔ)上+1的目的是這一位用來(lái)存儲(chǔ)負(fù)號(hào)。
由于stringSize方法要求傳入一個(gè)正整數(shù),所以代碼片段二在調(diào)用該方法時(shí)需要將負(fù)數(shù)轉(zhuǎn)成正數(shù)傳入。代碼片段一中,將-2147483648的值直接返回的原因就是整數(shù)最大只能表示2147483647,無(wú)法將stringSize(-i)中的i賦值成-2147483648。
getSize使用了的體系結(jié)構(gòu)知識(shí):
1.局部性原理之空間局部性:sizeTable為數(shù)組,存儲(chǔ)在相鄰的位置,cpu一次加載一個(gè)塊數(shù)據(jù)數(shù)據(jù)到cache中(多個(gè)數(shù)組數(shù)據(jù)),此后訪問(wèn)sizeTable 不需要訪問(wèn)內(nèi)存。
2.基于范圍的查找,是很實(shí)用的設(shè)計(jì)技術(shù)
片段三
getChars(i, size, buf);?
那么接下來(lái)就深入理解一下getChars方法。這部分我把關(guān)于這段代碼的分析直接寫(xiě)到注釋中,便于結(jié)合代碼理解。
static void getChars(int i, int index, char[] buf) {int q, r;int charPos = index;char sign = 0;if (i < 0) {sign = '-';i = -i;}// 每次循環(huán)過(guò)后,都會(huì)將i中的走后兩位保存到字符數(shù)組buf中的最后兩位中,讀者可以將數(shù)字i設(shè)置為12345678測(cè)試一下, //第一次循環(huán)結(jié)束之后,buf[7] = 8,buf[6]=7。第二次循環(huán)結(jié)束之后,buf[5] = 6,buf[4] = 5。while (i >= 65536) {q = i / 100;// really: r = i - (q * 100);r = i - ((q << 6) + (q << 5) + (q << 2));i = q;//取DigitOnes[r]的目的其實(shí)取數(shù)字r%10的結(jié)果buf [--charPos] = DigitOnes[r];//取DigitTens[r]的目的其實(shí)是取數(shù)字r/10的結(jié)果buf [--charPos] = DigitTens[r];}// Fall thru to fast mode for smaller numbers// assert(i <= 65536, i);//循環(huán)將其他數(shù)字存入字符數(shù)組中空余位置for (;;) {//這里其實(shí)就是除以10。取數(shù)52429和16+3的原因在后文分析。q = (i * 52429) >>> (16+3);// r = i-(q*10) ...r = i - ((q << 3) + (q << 1)); //將數(shù)字i的最后一位存入字符數(shù)組,//還是12345678那個(gè)例子,這個(gè)for循環(huán)第一次結(jié)束后,buf[3]=4。buf [--charPos] = digits [r];i = q;//for循環(huán)結(jié)束后,buf內(nèi)容為“12345678”;if (i == 0) break;}if (sign != 0) {buf [--charPos] = sign;}}//其中用到的幾個(gè)數(shù)組//100以內(nèi)的數(shù)字除以10的結(jié)果(取整),//比如取DigitTens[78],返回的是數(shù)字7//只要是70-79的數(shù)字,返回的都是7,依次類推,所以總結(jié)出規(guī)律,其實(shí)就是返回的對(duì)應(yīng)數(shù)字除10取整的結(jié)果。finalstaticchar[]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',};//100以內(nèi)的數(shù)字對(duì)10取模的結(jié)果,//比如取DigitTens[78],返回的8finalstaticchar[]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',};finalstaticchar[] 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'};?
接下來(lái)分析兩個(gè)問(wèn)題:
問(wèn)題一、為什么在getChars方法中,將整型數(shù)字寫(xiě)入到字符數(shù)組的過(guò)程中為什么按照數(shù)字65536分成了兩部分呢?這個(gè)65535是怎么來(lái)的?
部分一
while (i >= num1) {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 * num2) >>> (num3);r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...buf [--charPos] = digits [r];i = q;if (i == 0) break;}?
使用num1,num2,num3三個(gè)變量代替源代碼中的數(shù)字,便于后面分析使用。
問(wèn)題二、在上面兩段代碼的部分二中,在對(duì)i進(jìn)行除十操作的過(guò)程中為什么選擇先乘以52429在向右移位19位。其中52429和19是怎么來(lái)的?
解答
回答上面兩個(gè)問(wèn)題之前,首先要明確兩點(diǎn):
移位的效率比直接乘除的效率要高
乘法的效率比除法的效率要高
先理解以下代碼:
r = i - ((q << 6) + (q << 5) + (q << 2));表示的其實(shí)是r = i - (q * 100);,i-q*2^6 - q*2^5 - q*2^2?=?i-64q-32q-4q?=?i-100q。
q = (i * num2) >>> (num3);中,>>>表示無(wú)符號(hào)向右移位。代表的意義就是除以2^num3。 所以q = (i * 52429) >>> (16+3);?可以理解為:q = (i * 52429) / 524288;,那么就相當(dāng)于?q= i * 0.1也就是q=i/10,這樣通過(guò)乘法和向右以為的組合的形式代替了除法,能提高效率。
再來(lái)回答上面兩個(gè)問(wèn)題中,部分一和部分二中最大的區(qū)別就是部分一代碼使用了除法,第二部分只使用了乘法和移位。因?yàn)槌朔ê鸵莆坏男识家瘸ǜ?#xff0c;所以第二部分單獨(dú)使用了乘法加移位的方式來(lái)提高效率。那么為什么不都使用乘法加移位的形式呢?為什么大于num1(65536)的數(shù)字要使用除法呢?原因是int型變量最大不能超過(guò)(2^31-1)。如果使用一個(gè)太大的數(shù)字進(jìn)行乘法加移位運(yùn)算很容易導(dǎo)致溢出。那么為什么是65536這個(gè)數(shù)字呢?第二階段用到的乘法的數(shù)字和移位的位數(shù)又是怎么來(lái)的呢?
我們?cè)倩卮鸬诙€(gè)問(wèn)題。
既然我們要使用q = (i * num2) >>> (num3);的形式使用乘法和移位代替除法,那么n和m就要有這樣的關(guān)系:
num2= (2^num3 /10 +1)
只有這樣才能保證(i * num2) >>> (num3)結(jié)果接近于0.1。
那么52429這個(gè)數(shù)是怎么來(lái)的呢?來(lái)看以下數(shù)據(jù):
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 2^20=1048576, 104858/1048576=0.1000003815 2^21=2097152, 209716/2097152 = 0.1000003815 2^22= 4194304, 419431/4194304= 0.1000001431超過(guò)22的數(shù)字我就不列舉了,因?yàn)槿绻鹡um3越大,就會(huì)要求i比較小,因?yàn)楸仨毐WC(i * num2) >>> (num3)的過(guò)程不會(huì)因?yàn)橐绯龆鴮?dǎo)致數(shù)據(jù)不準(zhǔn)確。那么是怎么敲定num1=65536,num2= 524288, num3=19的呢? 這三個(gè)數(shù)字之間是有這樣一個(gè)操作的:
(num1* num2)>>> num3因?yàn)橐WC該操作不能因?yàn)橐绯鰧?dǎo)致數(shù)據(jù)不準(zhǔn)確,所以num1和num2就相互約束。兩個(gè)數(shù)的乘積是有一定范圍的,不成超過(guò)這個(gè)范圍,所以,num1增大,num2就要隨之減小。
我覺(jué)得有以下幾個(gè)原因:
1.52429/524288=0.10000038146972656精度足夠高。
2.下一個(gè)精度較高的num2和num3的組合是419431和22。2^31/2^22 = 2^9 = 512。512這個(gè)數(shù)字實(shí)在是太小了。65536正好是2^16,一個(gè)整數(shù)占4個(gè)字節(jié)。65536正好占了2個(gè)字節(jié),選定這樣一個(gè)數(shù)字有利于CPU訪問(wèn)數(shù)據(jù)。
不知道有沒(méi)有人發(fā)現(xiàn),其實(shí)65536* 52429是超過(guò)了int的最大值的,一旦超過(guò)就要溢出,那么為什么還能保證(num1* num2)>>> num3能得到正確的結(jié)果呢?
這和>>>有關(guān),因?yàn)?gt;>>表示無(wú)符號(hào)右移,他會(huì)在忽略符號(hào)位,空位都以0補(bǔ)齊。
一個(gè)有符號(hào)的整數(shù)能表示的范圍是-2147483648至2147483647,但是無(wú)符號(hào)的整數(shù)能表示的范圍就是0-4,294,967,296(2^32),所以,只要保證num2*num3的值不超過(guò)2^32次方就可以了。65536是2^16,52429正好小于2^16,所以,他們的乘積在無(wú)符號(hào)向右移位就能保證數(shù)字的準(zhǔn)確性。
getChars使用了的體系結(jié)構(gòu)知識(shí):
1.乘法比除法高效:q = ( i * 52429) >>> (16+3); => 約等于q0.1,但i52429是整數(shù)乘法器,結(jié)合位移避免除法。
2.重復(fù)利用計(jì)算結(jié)果:在獲取r(i%100)時(shí),充分利用了除法的結(jié)果,結(jié)合位移避免重復(fù)計(jì)算。
3.位移比乘法高效:r = i – (( q << 6) + ( q << 5) + ( q << 2)); = >等價(jià)于r = i – (q * 100);
4.局部性原理之空間局部性
(1).buf[–charPos] =DigitOnes[r];buf[–charPos] =DigitTens[r];通過(guò)查找數(shù)組,實(shí)現(xiàn)快速訪問(wèn),避免除法計(jì)算
(2).buf [–charPos ] = digits [ r];
片段四
return new String(buf, true);?
這里用到了一個(gè)String中提供的保護(hù)類型構(gòu)造函數(shù),關(guān)于此函數(shù)請(qǐng)查看String源碼分析,該函數(shù)比使用其他的構(gòu)造函數(shù)有更好的性能。
總結(jié)
所以,一個(gè)Integer對(duì)象有很多方法能夠?qū)⒅缔D(zhuǎn)成String類型。除了上面提到的一系列方法外,一般在要使用String的時(shí)候,很多人愿意使用如下形式:
Integer s = new Integer(199); System.out.println(s + "");?
老規(guī)矩,反編譯看看怎么實(shí)現(xiàn)的:
Integer s = new Integer(199); System.out.println((new StringBuilder()).append(s).append("").toString());?
筆者使用JMH進(jìn)行了測(cè)試,結(jié)果證明方法效率更高。
compareTo方法
在看是介紹Interger的類定義的時(shí)候介紹過(guò),Integer類實(shí)現(xiàn)了Comparable<Integer>接口,所以Integer對(duì)象可以和另外一個(gè)Integer對(duì)象進(jìn)行比較。
public int compareTo(Integer anotherInteger) {return compare(this.value, anotherInteger.value); }public static int compare(int x, int y) {return (x < y) ? -1 : ((x == y) ? 0 : 1); }?
代碼實(shí)現(xiàn)比較簡(jiǎn)單,就是拿出其中的int類型的value進(jìn)行比較。
實(shí)現(xiàn)Number的方法
int intValue(); long longValue(); float floatValue(); double doubleValue(); byte byteValue(); short shortValue();?
實(shí)現(xiàn)如下:
public long longValue() {return (long)value; }public float floatValue() {return (float)value; }public double doubleValue() {return (double)value; } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java Integer 源码学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 郑州连日高温 蜜雪冰城“黑化”:白色雪人
- 下一篇: 卖房后第二天就出拆迁公告!浙江男子崩溃: