Long,包裝類之間比較及其源碼分析
歡迎大家來我的個人博客:https://www.fxyh97.com/index.php/archives/197/
這段時間發現了一個Long類型比較的問題,原因就是Long和Long比較使用的是"==
"比較,而不是用的equals,因爲比較兩個id是否相等,一開始數據量沒有那麼多,id值也沒有很大,然後用==一直沒出現問題,也沒發現這個問題,現在數據多了就暴露了這個問題。
出現這個原因得從源碼說起吧,包裝類的介紹我就不說了,可以看我之前的JavaSE-JavaAPI之lang包
這裏我就直接開始進入正題了。
首先看幾個例子:
Long a = 127L;
Long b = 127L;
Long c = 128L;
Long d = 128L;
System.out.println("a == b:" + (a == b));
System.out.println("a.equals(b):" + (a.equals(b)));
System.out.println("c == d:" + (c == d));
System.out.println("c.equals(d):" + (c.equals(d)));
這樣的結果產生的原因還是用源碼解釋吧,下面是Long的源碼:
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
private static class LongCache {
//Long的內部類,在Long實例化的時候,就會把[-128,127]之間的數先new出來
private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
}
}
在源碼中可以看到,傳的參數在[-128,127]之間的,返回的是從LongCache.cache[]中取的一個Long,而在這之外的都是直接return new Long(l),而在java中,==
比較是直接比較對象的內存地址,而equals在Long中重寫的,是比較Long的數值,所以equals是數值一樣就爲true,而==
要內存地址一樣纔是true。
由此可見,我們再去看看其他的包裝類,Long已經說過了,下面就不列出了。
-
Byte
-
先貼源碼:
-
public static Byte valueOf(byte b) { final int offset = 128; return ByteCache.cache[(int)b + offset]; } public boolean equals(Object obj) { if (obj instanceof Byte) { return value == ((Byte)obj).byteValue(); } return false; } private static class ByteCache { // Byte內部內 private ByteCache(){} static final Byte cache[] = new Byte[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Byte((byte)(i - 128)); } }
-
-
因爲byte的值範圍爲[-128,127],所以他這裏就直接從內部內的數組中取的對象出來,不管怎麼樣都是沒有new 對象的,所以==比較和equals比較都是一樣的,畢竟內存地址都是一樣的。
-
-
Short
-
源碼如下:
-
public static Short valueOf(short s) { final int offset = 128; int sAsInt = s; if (sAsInt >= -128 && sAsInt <= 127) { // must cache return ShortCache.cache[sAsInt + offset]; } return new Short(s); } public boolean equals(Object obj) { if (obj instanceof Short) { return value == ((Short)obj).shortValue(); } return false; } private static class ShortCache { private ShortCache(){} static final Short cache[] = new Short[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Short((short)(i - 128)); } }
-
-
這個不用多說了吧。
-
-
Integer
-
源碼:
-
/** * A constant holding the minimum value an {@code int} can * have, -2<sup>31</sup>. * -2的31次方:-2147483648 */ @Native public static final int MIN_VALUE = 0x80000000; /** * A constant holding the maximum value an {@code int} can * have, 2<sup>31</sup>-1. * 2的31次方-1:2147483647 */ @Native public static final int MAX_VALUE = 0x7fffffff; public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; } private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; 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; } private IntegerCache() {} }
-
-
這個就說一下這個
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
這裏他的意思就是-Djava.lang.Integer.IntegerCache.high=XXX
- 這個XXX小於127,Integer緩存的常量池中的就是[-128,127]
- 這個XXX大於127,小於2的31次方-1(2147483647)緩存的常量池就是[-128,xxx]
- 這個XXX大於2的31次方-1緩存的常量池就是[-128,2147483647]
-
我的電腦配置不咋地就設置一個300來看看。
-
System.out.println("java.lang.Integer.IntegerCache.high = " + sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high")); Integer integer1 = -128; Integer integer2 = -128; Integer integer3 = -129; Integer integer4 = -129; Integer integer5 = 300; Integer integer6 = 300; Integer integer7 = 301; Integer integer8 = 301; System.out.println("-128 == -128:" + (integer1 == integer2)); System.out.println("-129 == -129:" + (integer3 == integer4)); System.out.println("300 == 300:" + (integer5 == integer6)); System.out.println("301 == 301:" + (integer7 == integer8));
- 結果應該看到很清楚了,我就不解釋了。
-
-
-
Float
-
源碼
-
public static Float valueOf(float f) { return new Float(f); } public boolean equals(Object obj) { return (obj instanceof Float) && (floatToIntBits(((Float)obj).value) == floatToIntBits(value)); }
-
-
Float是自動裝箱都會創建一個新對象,所以只能用equals比較數值相等,用==比較除非是同一個對象。
-
-
Double
-
源碼
-
public static Double valueOf(double d) { return new Double(d); } public boolean equals(Object obj) { return (obj instanceof Double) && (doubleToLongBits(((Double)obj).value) == doubleToLongBits(value)); }
-
-
Double和Float一樣的。
-
-
Character
-
源碼
-
public static Character valueOf(char c) { if (c <= 127) { // must cache return CharacterCache.cache[(int)c]; } return new Character(c); } private static class CharacterCache { private CharacterCache(){} static final Character cache[] = new Character[127 + 1]; static { for (int i = 0; i < cache.length; i++) cache[i] = new Character((char)i); } } public boolean equals(Object obj) { if (obj instanceof Character) { return value == ((Character)obj).charValue(); } return false; }
-
-
這裏的意思就是緩存了常量池[0,127]的int轉成char的字符。具體是啥也不知道是啥,也沒有必要了解這麼清楚,用equals比較就對了。
-
-
Boolean
-
源碼
-
public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false); public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } public boolean equals(Object obj) { if (obj instanceof Boolean) { return value == ((Boolean)obj).booleanValue(); } return false; }
-
-
這裏Boolean只有TRUE or FALSE而且自動裝箱也是這兩個靜態常量,所以內存地址肯定是一樣的。==和equals比較都是可以的。
-
再貼上一些測試用例吧:
public class App {
public static void main(String[] args) {
testShort();
testFloat();
testDouble();
testCharacter();
testBoolean();
printChar0to127();
}
public static void testShort() {
Short short1 = 127;
Short short2 = 127;
Short short3 = 128;
Short short4 = 128;
Short short5 = -128;
Short short6 = -128;
Short short7 = -129;
Short short8 = -129;
System.out.println("Short:127==127 ==》" + (short1 == short2));
System.out.println("Short:127 equals 127 ==》" + (short1.equals(short2)));
System.out.println("Short:128==128 ==》" + (short3 == short4));
System.out.println("Short:128 equals 128 ==》" + (short3.equals(short4)));
System.out.println("Short:-128==-128 ==》" + (short5 == short6));
System.out.println("Short:-128 equals -128 ==》" + (short5.equals(short6)));
System.out.println("Short:-129==-129 ==》" + (short7 == short8));
System.out.println("Short:-129 equals -129 ==》" + (short7.equals(short8)));
//Short:127==127 ==》true
//Short:127 equals 127 ==》true
//Short:128==128 ==》false
//Short:128 equals 128 ==》true
//Short:-128==-128 ==》true
//Short:-128 equals -128 ==》true
//Short:-129==-129 ==》false
//Short:-129 equals -129 ==》true
}
public static void testFloat(){
Float float1 = 1.0F;
Float float2 = 1.0F;
System.out.println("Float:1.0F==1.0F ==》" + (float1 == float2));
System.out.println("Float:1.0F equals 1.0F ==》" + (float1.equals(float2)));
//Float:1.0F==1.0F ==》false
//Float:1.0F equals 1.0F ==》true
}
public static void testDouble(){
Double double1 = 1.0;
Double double2 = 1.0;
System.out.println("Double:1.0==1.0 ==》" + (double1 == double2));
System.out.println("Double:1.0 equals 1.0 ==》" + (double1.equals(double2)));
//Double:1.0==1.0 ==》false
//Double:1.0 equals 1.0 ==》true
}
public static void testCharacter(){
Character character1 = (char)127;
Character character2 = (char)127;
Character character3 = (char)128;
Character character4 = (char)128;
Character character5 = (char)0;
Character character6 = (char)0;
Character character7 = (char)-1;
Character character8 = (char)-1;
System.out.println("Character:(char)127==(char)127 ==》" + (character1 == character2));
System.out.println("Character:(char)127 equals (char)127 ==》" + (character1.equals(character2)));
System.out.println("Character:(char)128==(char)128 ==》" + (character3 == character4));
System.out.println("Character:(char)128 equals (char)128 ==》" + (character3.equals(character4)));
System.out.println("Character:(char)0==(char)0 ==》" + (character5 == character6));
System.out.println("Character:(char)0 equals (char)0 ==》" + (character5.equals(character6)));
System.out.println("Character:(char)-1==(char)-1 ==》" + (character7 == character8));
System.out.println("Character:(char)-1 equals (char)-1 ==》" + (character7.equals(character8)));
//Character:(char)127==(char)127 ==》true
//Character:(char)127 equals (char)127 ==》true
//Character:(char)128==(char)128 ==》false
//Character:(char)128 equals (char)128 ==》true
//Character:(char)0==(char)0 ==》true
//Character:(char)0 equals (char)0 ==》true
//Character:(char)-1==(char)-1 ==》false
//Character:(char)-1 equals (char)-1 ==》true
}
public static void testBoolean(){
Boolean boolean1 = false;
Boolean boolean2 = false;
Boolean boolean3 = true;
Boolean boolean4 = true;
System.out.println("Boolean: false == false ==》" + (boolean1 == boolean2));
System.out.println("Boolean: false equals false ==》" + (boolean1.equals(boolean2)));
System.out.println("Boolean: true == true ==》" + (boolean3 == boolean4));
System.out.println("Boolean: true equals true ==》" + (boolean3.equals(boolean4)));
//Boolean: false == false ==》true
//Boolean: false equals false ==》true
//Boolean: true == true ==》true
//Boolean: true equals true ==》true
}
public static void printChar0to127(){
for (int i = 0; i <= 127; i++) {
System.out.print(i + ":" + (char)i + "\t");
if(i > 0 && i%10 == 0){
System.out.println();
}
}
//打印結果:
/*0: 1: 2: 3: 4: 5: 6: 7: 8 9: 10:
14: 15: 16: 17: 18: 19: 20:
21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
31: 32: 33:! 34:" 35:# 36:$ 37:% 38:& 39:' 40:(
41:) 42:* 43:+ 44:, 45:- 46:. 47:/ 48:0 49:1 50:2
51:3 52:4 53:5 54:6 55:7 56:8 57:9 58:: 59:; 60:<
61:= 62:> 63:? 64:@ 65:A 66:B 67:C 68:D 69:E 70:F
71:G 72:H 73:I 74:J 75:K 76:L 77:M 78:N 79:O 80:P
81:Q 82:R 83:S 84:T 85:U 86:V 87:W 88:X 89:Y 90:Z
91:[ 92:\ 93:] 94:^ 95:_ 96:` 97:a 98:b 99:c 100:d
101:e 102:f 103:g 104:h 105:i 106:j 107:k 108:l 109:m 110:n
111:o 112:p 113:q 114:r 115:s 116:t 117:u 118:v 119:w 120:x
121:y 122:z 123:{ 124:| 125:} 126:~ 127: */
}
}