Long,包裝類之間比較及其源碼分析

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已經說過了,下面就不列出了。

  1. Byte

    1. 先貼源碼:

      • 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));
            }
        }
        
    2. 因爲byte的值範圍爲[-128,127],所以他這裏就直接從內部內的數組中取的對象出來,不管怎麼樣都是沒有new 對象的,所以==比較和equals比較都是一樣的,畢竟內存地址都是一樣的。

  2. Short

    1. 源碼如下:

      • 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));
            }
        }
        
    2. 這個不用多說了吧。

  3. Integer

    1. 源碼:

      • /**
        * 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() {}
            }
        
    2. 這個就說一下這個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]
    3. 我的電腦配置不咋地就設置一個300來看看。

      1. 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));
        
      2. 結果應該看到很清楚了,我就不解釋了。
  4. Float

    1. 源碼

      • 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));
        }
        
    2. Float是自動裝箱都會創建一個新對象,所以只能用equals比較數值相等,用==比較除非是同一個對象。

  5. Double

    1. 源碼

      1. 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));
         }
        
    2. Double和Float一樣的。

  6. Character

    1. 源碼

      • 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;
        }
        
    2. 這裏的意思就是緩存了常量池[0,127]的int轉成char的字符。具體是啥也不知道是啥,也沒有必要了解這麼清楚,用equals比較就對了。

  7. Boolean

    1. 源碼

      • 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;
        }
        
    2. 這裏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:	*/
    }
}

包裝類比較最好就用equals比較,==比較可能出現了問題都不知道哪裏出的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章