java中equals,hashcode和==的区别

==

java中的数据类型,可分为两类:

1.基本数据类型,也称原始数据类型

byte,short,char,int,long,float,double,boolean 他们之间的比较,应用双等号(==),比较的是他们的值。

基本数据类型与其他内存==比较,其他类型都会拆箱转换成基本数据类型

2.引用类型(类、接口、数组)

当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。

对象是放在堆中的,栈中存放的是对象的引用(地址)。由此可见'=='是对栈中的值进行比较的。如果要比较堆中对象的内容是否相同,那么就要重写equals方法了。

	public static void main(String[] args) {
		int int1 = 12;
		int int2 = 12;
		Integer Integer1 = new Integer(12);
		Integer Integer2 = new Integer(12);
		Integer Integer3 = new Integer(127);

		Integer a1 = 127;
		Integer b1 = 127;

		Integer a = 128;
		Integer b = 128;

		String s1 = "str";
		String s2 = "str";
		String str1 = new String("str");
		String str2 = new String("str");

		System.out.println("int1==int2:" + (int1 == int2));
		System.out.println("int1==Integer1:" + (int1 == Integer1));
		System.out.println("Integer1==Integer2:" + (Integer1 == Integer2));
		System.out.println("Integer3==b1:" + (Integer3 == b1));
		System.out.println("a1==b1:" + (a1 == b1));
		System.out.println("a==b:" + (a == b));

		System.out.println("s1==s2:" + (s1 == s2));
		System.out.println("s1==str1:" + (s1 == str1));
		System.out.println("str1==str2:" + (str1 == str2));
	}

Output:

int1==int2:true
int1==Integer1:true  //Integer1会自动拆箱转换为int,Integer1==int1,也一样是true
Integer1==Integer2:false //不同对象,内存存放地址不同
Integer3==b1:false  //Integer3指向new的对象地址,b1指向缓存中127地址,地址不同,所以为false
a1==b1:true
a==b:false
s1==s2:true
s1==str1:false
str1==str2:false

Integer b1 = 127;java在编译的时候,被翻译成-> Integer b1 = Integer.valueOf(127);

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

IntegerCache:

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() {}
    }

我们看到IntegerCache的low定义为-128,high默认定义为127.但是high是可以配置的,如果没有配置才是127.我们不去看配置的情况,因为java默认是没有配置的。看一下cache数组,长度为high-low+1,从-128开始到127,存在cache数组内。

从上面的代码中可以看出,java在申请一个大于-128小于127的数时,其实是从cache中直接取出来用的,如果不在这个范围则是new了一个Integer对象。

对于==,他比较的是地址。对于int来说比较的是值。

对于equals,比较的是内容(要看equals的具体实现)。看一下Integer里面的实现:

  public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

只要都是Integer类型,equals比较的也是值

Byte,Short,Long 的缓存池范围默认都是: -128 到 127。可以看出,Byte的所有值都在缓存区中,用它生成的相同值对象都是相等的。

equals

默认情况(没有覆盖equals方法)下equals方法都是调用Object类的equals方法,而Object的equals方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。

下面是Object类中equals方法:

public boolean equals(Object obj) {
    return (this == obj);
}

定义的equals与==是等效的

要是类中覆盖了equals方法,那么就要根据具体的代码来确定equals方法的作用了,覆盖后一般都是通过对象的内容是否相等来判断对象是否相等。

hasCode

hashCode()方法返回的就是一个数值,从方法的名称上就可以看出,其目的是生成一个hash码。

equals与hasCode关联

  • 如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。
  • 如果两个对象不equals,他们的hashcode有可能相等。
  • 如果两个对象hashcode相等,他们不一定equals。
  • 如果两个对象hashcode不相等,他们一定不equals。

为什么覆盖equals时总要覆盖hashCode

在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap、HashSet和Hashtable。

因为如果不覆盖equals方法的话,相等的对象可能返回的不相同的hash code。

比如上面这种情况就会出现在hashmap中,

一个对象修改equals,判断里面的字段是否一致即相等,但是没有修改hasCode

hashmap中的key根据hashCode分配到不同的元素,但是用equals尝试,key却是相等的,那就恶心了,会出现很多异常bug.

案例

	public static void main(String[] args) throws Exception {
		Integer a = 1;
		Integer b = 2;
		Integer c = 3;
		Integer d = 3;
		Integer e = 321;
		Integer f = 321;
		Long g = 3L;
		Long h = 2L;

		System.out.println(c==d);
		System.out.println(e==f);
		System.out.println(c==(a+b));
		System.out.println(c.equals(a+b));
		System.out.println(g==(a+b));
		System.out.println(g.equals(a+b));
		System.out.println(g.equals(a+h));
	}

output:

true
false
true
true
true
false
true

第一个和第二个输出结果没有什么疑问。第三句由于 a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

总结

  • equals对象操作的两侧都必须为对象,如果有一边有基本数据类型,需要进行装箱操作
  • 直接使用运算符(+,-,*,/...),必须左右两侧都为基本数据类型,如果不是,需要进行拆箱操作
  • equals比较的是Object类型,虽然比较的是数值,但是如果两边类型不一致,也会返回false
  • 通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象
  • Integer a=3;与Integer a=new Integer(3);区别是第一种方式在[-128,127]性能稍微好点,指向IntegerCache.cache中,第二种方式是新建一个Integer对象。

参考:

java中equals,hashcode和==的区别

JAVA 拾遺--eqauls 和 hashCode 方法

覆盖equals时总要覆盖hashCode

深入剖析Java中的装箱和拆箱

posted @ 2019-01-15 15:14  hongdada  阅读(1119)  评论(0编辑  收藏  举报