java中的equals,hashcode和==的区别

1、==

Java中的数据类型可分为两类

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

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


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

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

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

public static void main(String[] int3rgs) {
        int num1 = 12;
        int num2 = 12;
        Integer in1 = new Integer(12);
        Integer in2 = new Integer(12);
        Integer in3 = new Integer(127);
        Integer int1 = 127;
        Integer int2 = 127;

        Integer int3 = 128;
        Integer int4 = 128;

        System.out.println("num1 == num2:\t" + (num1 == num2));
        System.out.println("num1 ==  in1:\t" + (num1 == in1));
        System.out.println("in1 ==  in2:\t" + (in1 == in2));
        System.out.println("in3 == int1:\t" + (in3 == int1));
        System.out.println("int1 == int2:\t" + (int1 == int2));
        System.out.println("int3 == int4:\t" + (int3 == int4));


        //String   经典的引用类型
        String s1 = "str";
        String s2 = "str";
        String str1 = new String("str");
        String str2 = new String("str");

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

    }

输出结果为:
num1 == num2:	true  
num1 ==  in1:	true	//Integer会自动拆箱为int,所以为true
in1 ==  in2:	false	//不同对象,在内存存放地址不同,所以为false
in3 == int1:	false	//Integer3指向new的对象地址,b1指向缓存中127地址,地址不同,所以为false
int1 == int2:	true	
int3 == int4:	false
s1 == s2:	true
str1 == str2:	false
s1 == str1:	false
Integer int1 = 127;java在编译的时候,被翻译成-> Integer int1 = 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);
    }
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() {}
    }
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}


对于-128127之间的数会进行缓存,Integer
 int1 = 127会将127进行缓存下次再写Integer
 int2 = 127就会直接从缓存中取就不会new所以int1==int2:true 
  而int3 跟 int4 每次都会重新new 所以    int3==int4:false 因为他们的引用地址不一样

2、equals

1、默认情况没有覆盖equals方法equals方法都是调用Object类的equals方法Objectequals方法主要用于判断对象的内存地址引用是不是同一个地址是不是同一个对象下面是Object类中equals方法
public boolean equals(Object obj) {  
	return (this == obj);  
}  
定义的equals==是等效的
2 、要是类中覆盖了equals方法那么就要根据具体的代码来确定equals方法的作用了覆盖后一般都是通过对象的内容是否相等来判断对象是否相等下面是String类对equals进行了重写:
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

Stringequals方法判断相等的步骤是

1.A==B 即是同一个String对象 返回true

2.若对比对象是String类型则继续否则返回false

3.判断A、B长度是否一样不一样的话返回false

4。逐个字符比较若有不相等字符返回false

这里对equals重新需要注意五点
1   自反性对任意引用值X,x.equals(x)的返回值一定为true. 
2   对称性对于任何引用值x,y,当且仅当y.equals(x)返回值为true,x.equals(y)的返回值一定为true; 
3   传递性如果x.equals(y)=true, y.equals(z)=true,x.equals(z)=true 
4   一致性如果参与比较的对象没任何改变则对象比较的结果也不应该有任何改变 
5   非空性任何非空的引用值X,x.equals(null)的返回值一定为false 

 

实现高质量equals方法的诀窍 
1.使用==符号检查参数是否为这个对象的引用如果是则返回true。这只不过是一种性能优化如果比较操作有可能很昂贵就值得这么做。 
2.使用instanceof操作符检查参数是否为正确的类型如果不是则返回false。一般来说所谓正确的类型是指equals方法所在的那个类。 
3.把参数转换成正确的类型因为转换之前进行过instanceof测试所以确保会成功。 
4.对于该类中的每个关键检查参数中的域是否与该对象中对应的域相匹配如果这些测试全部成功则返回true;否则返回false。 
5.当编写完成了equals方法之后检查对称性”、“传递性”、“一致性”。 

 

3、hashCode

hashCode()方法返回的就是一个数值从方法的名称上就可以看出其目的是生成一个hash。hash码的主要用途就是在对对象进行散列的时候作为key输入据此很容易推断出我们需要每个对象的hash码尽可能不同这样才能保证散列的存取性能事实上,Object类提供的默认实现确实保证每个对象的hash码不同在对象的内存地址基础上经过特定算法返回一个hash)。Java采用了哈希表的原理哈希(Hash)实际上是个人名由于他提出一哈希算法的概念所以就以他的名字命名了哈希算法也称为散列算法将数据依特定算法直接指定到一个地址上初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址实际可能并不是)。   

散列函数,散列算法,哈希函数
是一种从任何一种数据中创建小的数字指纹的方法
散列函数将任意长度的二进制值映射为较短的固定长度的二进制值这个小的二进制值称为哈希值
好的散列函数在输入域中很少出现散列冲突
=================================================================================
所有散列函数都有如下一个基本特性
1:如果a=b,h(a) = h(b)。
2:如果a!=b,h(a)h(b)可能得到相同的散列值

Object hashCode方法返回一个int类型
public native int hashCode();

3.1 hashCode的作用

想要明白必须要先知道Java中的集合。   
总的来说,Java中的集合(Collection)有两类一类是List,再有一类是Set。前者集合内的元素是有序的元素可以重复后者元素无序但元素不可重复

那么这里就有一个比较严重的问题了要想保证元素不重复可两个元素是否重复应该依据什么来判断呢 

这就是Object.equals方法了但是如果每增加一个元素就检查一次那么当元素很多时后添加到集合中的元素比较的次数就非常多了也就是说如果集合中现在已经有1000个元素那么第1001个元素加入集合时它就要调用1000equals方法这显然会大大降低效率。   
于是Java采用了哈希表的原理  

 

这样一来当集合要添加新的元素时

先调用这个元素的hashCode方法就一下子能定位到它应该放置的物理位置上。 

如果这个位置上没有元素它就可以直接存储在这个位置上不用再进行任何比较了

如果这个位置上已经有元素了就调用它的equals方法与新元素进行比较相同的话就不存不相同就散列其它的地址所以这里存在一个冲突解决的问题这样一来实际调用equals方法的次数就大大降低了几乎只需要一两次

 

4、eqauls方法和hashCode方法关系

Java对于eqauls方法和hashCode方法是这样规定的: 

(1)同一对象上多次调用hashCode()方法总是返回相同的整型值

(2)如果a.equals(b),则一定有a.hashCode() 一定等于 b.hashCode()。 

(3)如果!a.equals(b),a.hashCode() 不一定等于 b.hashCode()。此时如果a.hashCode() 总是不等于 b.hashCode(),会提高hashtables的性能

(4)a.hashCode()==b.hashCode()  a.equals(b)可真可假

(5)a.hashCode()!= b.hashCode()  a.equals(b)为假。 

 

上面结论简记

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

 

关于这两个方法的重要规范: 

规范1:若重写equals(Object obj)方法有必要重写hashcode()方法确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值说得简单点就是:“如果两个对象相同那么他们的hashcode应该相等”。不过请注意这个只是规范如果你非要写一个类让equals(Object obj)返回truehashcode()返回两个不相等的值编译和运行都是不会报错的不过这样违反了Java规范程序也就埋下了BUG。 

规范2:如果equals(Object obj)返回false,即两个对象不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数说的简单点就是:“如果两个对象不相同他们的hashcode可能相同”。 

 

5、为什么覆盖equals时总要覆盖hashCode 
 一个很常见的错误根源在于没有覆盖hashCode方法在每个覆盖了equals方法的类中也必须覆盖hashCode方法如果不这样做的话就会违反Object.hashCode的通用约定从而导致该类无法结合所有基于散列的集合一起正常运作这样的集合包括HashMap、HashSetHashtable。

1.在应用程序的执行期间只要对象的equals方法的比较操作所用到的信息没有被修改那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数在同一个应用程序的多次执行过程中每次执行所返回的整数可以不一致

2.如果两个对象根据equals()方法比较是相等的那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果

3.如果两个对象根据equals()方法比较是不相等的那么调用这两个对象中任意一个对象的hashCode方法则不一定要产生相同的整数结果但是程序员应该知道给不相等的对象产生截然不同的整数结果有可能提高散列表的性能

 

6、总结
1、equals方法用于比较对象的内容是否相等覆盖以后

2、hashcode方法只有在集合中用到

3、当覆盖了equals方法时比较对象是否相等将通过覆盖后的equals方法进行比较判断对象的内容是否相等)。

4、将对象放入到集合中时首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等如果不相等直接将该对象放入集合中如果hashcode值相等然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等如果equals判断不相等直接将该元素放入到集合中否则不放入




大神们请多多指正我的不对~~~~`

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值