Long,包装类之间比较及其源码分析
这段时间发现了一个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: */
}
}