其餘更多java基礎文章: java基礎學習(目錄)java
轉載自 Java 源碼學習系列(三)——Integer
學習的過程當中發現這位大神的文章已經很是全面了,若是以後學習還有補充的,我會寫一個源碼分析(二) 有什麼疑問能夠在評論裏問git
Integer 類在對象中包裝了一個基本類型 int 的值。Integer 類型的對象包含一個 int 類型的字段。
此外,該類提供了多個方法,能在 int 類型和 String 類型之間互相轉換,還提供了處理 int 類型時很是有用的其餘一些常量和方法。程序員
public final class Integer extends Number implements Comparable<Integer>
數組
從類定義中咱們能夠知道如下幾點:緩存
一、Integer類不能被繼承
二、Integer類實現了Comparable接口,因此能夠用compareTo進行比較而且Integer對象只能和Integer類型的對象進行比較,不能和其餘類型比較(至少調用compareTo方法沒法比較)。
三、Integer繼承了Number類,因此該類能夠調用longValue、floatValue、doubleValue等系列方法返回對應的類型的值。bash
Integer類中定義瞭如下幾個私有屬性:app
private final int value;
private static final long serialVersionUID = 1360826667806852920L;
複製代碼
serialVersionUID和序列化有關。函數
由於Integer實現了Serializable接口,因此支持序列化和反序列化支持。Java的序列化機制是經過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,若是相同就認爲是一致的,能夠進行反序列化,不然就會出現序列化版本不一致的異常(InvalidCastException)。源碼分析
還有一個私有屬性——value屬性就是Integer對象中真正保存int值的。post
當咱們使用new Integer(10)
建立一個Integer對象的時候,就會用如下形式給value賦值。還有其餘的構造函數在後面會講。
public Integer(int value) {
this.value = value;
}
複製代碼
這裏咱們討論一下Interger對象的可變性。從value的定義形式中能夠看出value被定義成final類型。也就說明,一旦一個Integer對象被初始化以後,就沒法再改變value的值。那麼這裏就深刻討論一下如下代碼的邏輯:
/**
* Created by hollis on 16/1/22.
*/
public class IntegerTest {
public static void main(String[] args) {
Integer i = new Integer(10);
i = 5;
}
}
複製代碼
在以上代碼中,首先調用構造函數new一個Integer對象,給私有屬性value賦值,這時value=10
,接下來使用i=5
的形式試圖改變i的值。有一點開發經驗的同窗都知道,這個時候若是使用變量i,那麼它的值必定是5,那麼i=5
這個賦值操做到底作了什麼呢?究竟是如何改變i的值的呢?是改變了原有對象i中value的值仍是從新建立了一個新的Integer對象呢?
咱們將上面的代碼進行反編譯,反編譯以後的代碼以下:
public class IntegerTest
{
public IntegerTest()
{
}
public static void main(String args[])
{
Integer i = new Integer(10);
i = Integer.valueOf(5);
}
}
複製代碼
經過看反編譯以後的代碼咱們發現,編譯器會把i=5
轉成i = Integer.valueOf(5);
這裏先直接給出結論,i=5操做並無改變使用Integer i = new Integer(10);
建立出來的i中的value屬性的值。要麼是直接返回一個已有對象,要麼新建一個對象。這裏的具體實現細節在後面講解valueOf
方法的時候給出。
//值爲 (-(2的31次方)) 的常量,它表示 int 類型可以表示的最小值。
public static final int MIN_VALUE = 0x80000000;
//值爲 ((2的31次方)-1) 的常量,它表示 int 類型可以表示的最大值。
public static final int MAX_VALUE = 0x7fffffff;
//表示基本類型 int 的 Class 實例。
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
//用來以二進制補碼形式表示 int 值的比特位數。
public static final int SIZE = 32;
//用來以二進制補碼形式表示 int 值的字節數。1.8之後纔有
public static final int BYTES = SIZE / Byte.SIZE;
複製代碼
以上屬性可直接使用,由於他們已經定義成publis static fianl
能用的時候儘可能使用他們,這樣不只能使代碼有很好的可讀性,也能提升性能節省資源。
Integer提供了兩個構造方法:
//構造一個新分配的 Integer 對象,它表示指定的 int 值。
public Integer(int value) {
this.value = value;
}
//構造一個新分配的 Integer 對象,它表示 String 參數所指示的 int 值。
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
複製代碼
從構造方法中咱們能夠知道,初始化一個Integer對象的時候只能建立一個十進制的整數。
前面說到Integer中私有屬性value的時候提到
Integer i = new Integer(10);
i = 5;
複製代碼
其中i=5
操做時,編譯器會轉成i = Integer.valueOf(5);
執行。那麼這裏就解釋一下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方法的實現細節。一般狀況下,IntegerCache.low=-128,IntegerCache.high=127(除非顯示聲明java.lang.Integer.IntegerCache.high的值),Integer中有一段動態代碼塊,該部份內容會在Integer類被加載的時候就執行。
static {
// high value may be configured by property
int 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_VALUE
h = 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;
}
複製代碼
也就是說,當Integer被加載時,就新建了-128到127的全部數字並存放在Integer數組cache中。
再回到valueOf代碼,能夠得出結論。當調用valueOf方法(包括後面會提到的重載的參數類型包含String的valueOf方法)時,若是參數的值在-127到128之間,則直接從緩存中返回一個已經存在的對象。若是參數的值不在這個範圍內,則new一個Integer對象返回。
因此,當把一個int變量轉成Integer的時候(或者新建一個Integer的時候),建議使用valueOf方法來代替構造函數。或者直接使用Integer i = 100;
編譯器會轉成Integer s = Integer.valueOf(100);
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)
複製代碼
以上全部方法都能實現將String類型的值轉成Integer(int)類型(若是 String 不包含可解析整數將拋出NumberFormatException)
能夠說,全部將String轉成Integer的方法都是基於parseInt方法實現的。簡單看一下以上部分方法的調用棧。
getInteger(String nm) ---> getInteger(nm, null);--->Integer.decode()--->Integer.valueOf()--->parseInt()
複製代碼
肯定具備指定名稱的系統屬性的整數值。 第一個參數被視爲系統屬性的名稱。經過 System.getProperty(java.lang.String)
方法能夠訪問系統屬性。而後,將該屬性的字符串值解釋爲一個整數值,並返回表示該值的 Integer 對象。使用 getProperty
的定義能夠找到可能出現的數字格式的詳細信息。其中參數nm應該在System的props中能夠找到。這個方法在平常編碼中很好是用到。在代碼中能夠用如下形式使用該方法:
Properties props = System.getProperties();
props.put("hollis.integer.test.key","10000");
Integer i = Integer.getInteger("hollis.integer.test.key");
System.out.println(i);
//輸出 10000
複製代碼
另外兩個方法
getInteger(String nm,int val)
getInteger(String nm, Integer val)
複製代碼
第二個參數是默認值。若是未具備指定名稱的屬性,或者屬性的數字格式不正確,或者指定名稱爲空或 null,則返回默認值。
getInteger的具體實現細節以下:
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從系統配置中取出值,而後調用Integer.decode方法將其轉換成整數並返回。
public static Integer decode(String nm) throws NumberFormatException
複製代碼
該方法的做用是將 String 解碼爲 Integer。接受十進制、十六進制和八進制數字。
根據要解碼的 String(mn)的形式轉成不一樣進制的數字。 mn由三部分組成:符號、基數說明符和字符序列。 —0X123
中-
是符號位,0X
是基數說明符(0表示八進制,0x,0X,#表示十六進制,什麼都不寫則表示十進制),123
是數字字符序列。
使用例子舉例以下:
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方法的具體實現也比較簡單,首先就是判斷String類型的參數mn是否以(+/—)符號開頭。而後再依次判斷是否以」0x」、「#」、「0」開頭,肯定基數說明符的值。而後將字符串mn進行截取,只保留其中純數字部分。在用截取後的純數字和基數調用valueOf(String s, int radix)
方法並返回其值。
public static Integer valueOf(String s) throws NumberFormatException
public static int parseInt(String s, int radix) throws NumberFormatException
複製代碼
返回一個 Integer 對象。若是指定第二個參數radix,將第一個參數解釋爲用第二個參數指定的基數表示的有符號整數。若是沒指定則按照十進制進行處理。
該方法實現很是簡單:
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));
}
複製代碼
主要用到了兩個方法,parseInt(String s, int radix)
和valueOf(int i)
方法。前面已經講過valueOf方法會檢查參數內容是否在-127到128之間,若是是則直接返回。不然纔會新建一個對象。
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
public static int parseInt(String s, int radix) throws NumberFormatException
複製代碼
使用第二個參數指定的基數(若是沒指定,則按照十進制處理),將字符串參數解析爲有符號的整數。除了第一個字符能夠是用來表示負值的 ASCII 減號 ‘-‘ (‘\u002D’)外,字符串中的字符必須都是指定基數的數字(經過 Character.digit(char, int) 是否返回一個負值肯定)。返回獲得的整數值。
若是發生如下任意一種狀況,則拋出一個 NumberFormatException
類型的異常:
第一個參數爲 null 或一個長度爲零的字符串。
基數小於 Character.MIN_RADIX 或者大於 Character.MAX_RADIX。
假如字符串的長度超過 1,那麼除了第一個字符能夠是減號 ‘-‘ (‘u002D’) 外,字符串中存在任意不是由指定基數的數字表示的字符.
字符串表示的值不是 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) 拋出 NumberFormatException
parseInt("ADMIN", 27) 返回 5586836
複製代碼
該方法的具體實現方式也比較簡單,主要邏輯代碼(省略部分參數校驗)以下:
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;
}
複製代碼
主要思想其實也很好理解。
「12345」按照十進制轉成12345的方法其實就是如下方式: ((1*10)+2)*10)+3)*10+4)*10+5
具體的如何依次取出「12345」中的每個字符並將起轉成不一樣進制int類型則是Character.digit方法實現的,這裏就不深刻講解了。
上面列舉了不少可以將String轉成Integer的方法。那麼他們之間有哪些區別,又該如何選擇呢?
parseInt方法返回的是基本類型int
其餘的方法返回的是Integer
valueOf(String)方法會調用valueOf(int)方法。
若是隻須要返回一個基本類型,而不須要一個對象,能夠直接使用Integert.parseInt("123");
若是須要一個對象,那麼建議使用valueOf()
,由於該方法能夠藉助緩存帶來的好處。
若是和進制有關,那麼就是用decode
方法。
若是是從系統配置中取值,那麼就是用getInteger
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方法的定義比較簡單,就是把一個int類型的數字轉換成字符串類型,可是這個方法的實現調用了一系列方法,經過閱讀這個方法,你就會對sun公司的程序員產生油然的敬佩。
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方法分解爲如下幾個片斷:
片斷一:
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";
複製代碼
這裏先對i的值作檢驗,若是等於Int能表示的最小值,則直接返回最小值的字符串形式。那麼爲何-2147483648要特殊處理呢?請看代碼片斷二的分析。
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
複製代碼
這段代碼的主要目的是體取出整數i的位數,並建立一個字符數組。 其中提取I的位數使用stringSize方法,這個方法實現以下:
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;
}
複製代碼
該方法要求傳入一個正整數,若是傳入的數字x的值是10000,那麼由於他大於9,99,999,9999,小於99999.因此他會返回99999在整型數組sizeTable中的下標」4″+1 = 5。咱們看10000這個數字的位數也確實是5。因此,就實現了返回一個正整數的位數。
設置size時,當i<0的時候返回的size數組在stringSize方法的基礎上+1的目的是這一位用來存儲負號。
因爲stringSize方法要求傳入一個正整數,因此代碼片斷二在調用該方法時須要將負數轉成正數傳入。代碼片斷一中,將-2147483648的值直接返回的緣由就是整數最大隻能表示2147483647,沒法將
stringSize(-i)
中的i賦值成-2147483648。
1.局部性原理之空間局部性:sizeTable爲數組,存儲在相鄰的位置,cpu一次加載一個塊數據數據到cache中(多個數組數據),此後訪問sizeTable 不須要訪問內存。
2.基於範圍的查找,是很實用的設計技術
getChars(i, size, buf);
複製代碼
那麼接下來就深刻理解一下getChars方法。這部分我把關於這段代碼的分析直接寫到註釋中,便於結合代碼理解。
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) {
sign = '-';
i = -i;
}
// 每次循環事後,都會將i中的走後兩位保存到字符數組buf中的最後兩位中,讀者能夠將數字i設置爲12345678測試一下,
//第一次循環結束以後,buf[7] = 8,buf[6]=7。第二次循環結束以後,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]的目的其實取數字r%10的結果
buf [--charPos] = DigitOnes[r];
//取DigitTens[r]的目的實際上是取數字r/10的結果
buf [--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
//循環將其餘數字存入字符數組中空餘位置
for (;;) {
//這裏其實就是除以10。取數52429和16+3的緣由在後文分析。
q = (i * 52429) >>> (16+3);
// r = i-(q*10) ...
r = i - ((q << 3) + (q << 1));
//將數字i的最後一位存入字符數組,
//仍是12345678那個例子,這個for循環第一次結束後,buf[3]=4。
buf [--charPos] = digits [r];
i = q;
//for循環結束後,buf內容爲「12345678」;
if (i == 0) break;
}
if (sign != 0) {
buf [--charPos] = sign;
}
}
//其中用到的幾個數組
//100之內的數字除以10的結果(取整),
//好比取DigitTens[78],返回的是數字7
//只要是70-79的數字,返回的都是7,依次類推,因此總結出規律,其實就是返回的對應數字除10取整的結果。
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',
} ;
//100之內的數字對10取模的結果,
//好比取DigitTens[78],返回的8
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',
} ;
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'
};
複製代碼
接下來分析兩個問題:
部分一
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三個變量代替源代碼中的數字,便於後面分析使用。
回答上面兩個問題以前,首先要明確兩點:
移位的效率比直接乘除的效率要高
乘法的效率比除法的效率要高
先理解如下代碼:
r = i - ((q << 6) + (q << 5) + (q << 2));
表示的實際上是r = i - (q * 100);
,i-q*2^6 - q*2^5 - q*2^2
= i-64q-32q-4q
= i-100q
。
q = (i * num2) >>> (num3);
中,>>>
表示無符號向右移位。表明的意義就是除以2^num3。 因此q = (i * 52429) >>> (16+3);
能夠理解爲:q = (i * 52429) / 524288;
,那麼就至關於 q= i * 0.1
也就是q=i/10
,這樣經過乘法和向右覺得的組合的形式代替了除法,能提升效率。
再來回答上面兩個問題中,部分一和部分二中最大的區別就是部分一代碼使用了除法,第二部分只使用了乘法和移位。由於乘法和移位的效率都要比除法高,因此第二部分單獨使用了乘法加移位的方式來提升效率。那麼爲何不都使用乘法加移位的形式呢?爲何大於num1(65536)的數字要使用除法呢?緣由是int型變量最大不能超過(2^31-1)。若是使用一個太大的數字進行乘法加移位運算很容易致使溢出。那麼爲何是65536這個數字呢?第二階段用到的乘法的數字和移位的位數又是怎麼來的呢?
咱們再回答第二個問題。
既然咱們要使用q = (i * num2) >>> (num3);的形式使用乘法和移位代替除法,那麼n和m就要有這樣的關係:
num2= (2^num3 /10 +1)
只有這樣才能保證(i * num2) >>> (num3)結果接近於0.1。
那麼52429這個數是怎麼來的呢?來看如下數據:
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
複製代碼
超過22的數字我就不列舉了,由於若是num3越大,就會要求i比較小,由於必須保證(i * num2) >>> (num3)
的過程不會由於溢出而致使數據不許確。那麼是怎麼敲定num1=65536,num2= 524288, num3=19的呢? 這三個數字之間是有這樣一個操做的:
(num1* num2)>>> num3
複製代碼
由於要保證該操做不能由於溢出致使數據不許確,因此num1和num2就相互約束。兩個數的乘積是有必定範圍的,不成超過這個範圍,因此,num1增大,num2就要隨之減少。
我以爲有如下幾個緣由:
1.
52429/524288=0.10000038146972656
精度足夠高。2.下一個精度較高的num2和num3的組合是419431和22。2^31/2^22 = 2^9 = 512。512這個數字實在是過小了。65536正好是2^16,一個整數佔4個字節。65536正好佔了2個字節,選定這樣一個數字有利於CPU訪問數據。
不知道有沒有人發現,其實65536* 52429
是超過了int的最大值的,一旦超過就要溢出,那麼爲何還能保證(num1* num2)>>> num3
能獲得正確的結果呢?
這和>>>
有關,由於>>>
表示無符號右移,他會在忽略符號位,空位都以0補齊。
一個有符號的整數能表示的範圍是-2147483648至2147483647,可是無符號的整數能表示的範圍就是0-4,294,967,296(2^32 ),因此,只要保證num2*num3的值不超過2^32 次方就能夠了。65536是2^16 ,52429正好小於2^16 ,因此,他們的乘積在無符號向右移位就能保證數字的準確性。
1.乘法比除法高效:q = ( i * 52429) >>> (16+3); => 約等於q0.1,但i52429是整數乘法器,結合位移避免除法。
2.重複利用計算結果:在獲取r(i%100)時,充分利用了除法的結果,結合位移避免重複計算。
3.位移比乘法高效:r = i – (( q << 6) + ( q << 5) + ( q << 2)); = >等價於r = i – (q * 100);
4.局部性原理之空間局部性
(1).buf[–charPos] =DigitOnes[r];buf[–charPos] =DigitTens[r];經過查找數組,實現快速訪問,避免除法計算
(2).buf [–charPos ] = digits [ r];
return new String(buf, true);
複製代碼
這裏用到了一個String中提供的保護類型構造函數,關於此函數請查看java基礎:String — 源碼分析(一) ,該函數比使用其餘的構造函數有更好的性能。
因此,一個Integer對象有不少方法可以將值轉成String類型。除了上面提到的一系列方法外,通常在要使用String的時候,不少人願意使用以下形式:
Integer s = new Integer(199);
System.out.println(s + "");
複製代碼
老規矩,反編譯看看怎麼實現的:
Integer s = new Integer(199);
System.out.println((new StringBuilder()).append(s).append("").toString());
複製代碼
筆者使用JMH進行了測試,結果證實方法效率更高。
在看是介紹Interger的類定義的時候介紹過,Integer類實現了Comparable<Integer>
接口,因此Integer對象能夠和另一個Integer對象進行比較。
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);
}
複製代碼
代碼實現比較簡單,就是拿出其中的int類型的value進行比較。
int intValue();
long longValue();
float floatValue();
double doubleValue();
byte byteValue();
short shortValue();
複製代碼
實現以下:
public long longValue() {
return (long)value;
}
public float floatValue() {
return (float)value;
}
public double doubleValue() {
return (double)value;
}
複製代碼