Long,包装类之间比较及其源码分析

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已经说过了,下面就不列出了。
  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比较,==比较可能出现了问题都不知道哪里出的问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值