java ==、equals和hashcode() 知识详解 (java面试)

前言

作为java高频面试点和java基础知识,==、equals和hashcode的相关知识总是会被面试官问到,本文将详细全面介绍相关知识点,相信会对你面试有所帮助!

一、==

1、概念:

“==” 的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。

对于基本数据类型 == 比较的是值
对于引用数据类型 == 比较的是内存地址

2、代码示例:

public static void main(String[] args) {
    String s1 = "str1";
    String s2 = "str1";
    String s3 = new String("str1");
    System.out.println(s1 == s2);    //    true
    System.out.println(s1 == s3);    //    false        		           
}

3、代码分析:

(1)s1 == s2为true:

s1和s2都是字符串字面值"str1"的引用,指向同一块地址,所以相等

(2)s1 == s3为false:

通过new产生的对象在堆中,s3是堆中变量的引用,而是s1是指向字符串字面值"str1"的引用,地址不同所以不相等

二、equals:

1、概念:

equals是根类Obeject中的方法。源代码如下:

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

从源代码可以看出,默认的equals方法,直接调用==,比较对象地址
不同的子类,可以重写此方法,进行两个对象的equals的判断;

2、equals的作用:

也是判断两个对象是否相等。但它一般有两种使用情况:

(1)类没有覆盖 equals() 方法:

通过 equals() 比较该类的两个对象时,等价于上述所说的通过“==”比较这两个对象

(2)类覆盖了 equals() 方法:

一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)

3、String类源码重写的equals方法:

(1)源码:

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;
    }

(2)源码分析:

String类中的equals首先比较地址,如果是同一个对象的引用,可知对象相等,返回true;
若果不是同一个对象,equals方法挨个比较两个字符串对象内的字符,只有完全相等才返回true,否则返回false

4、代码示例:

public static void main(String[] args) {
        String s1 = new String("ab"); // s1 为一个引用
        String s2 = new String("ab"); // s2为另一个引用,对象的内容一样
        String s3 = "ab"; // 放在常量池中
        String s4 = "ab"; // 从常量池中查找
        System.out.println(s3 == s4) // true
        System.out.println(s1 == s2) // false,非同一对象          
        System.out.println(s1.equals(s2)) // true          
        System.out.println(42 == 42.0) { // true            
        }
    }

5、示例分析:

(1)String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
(2)当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象

三、hashCode():

1、概念:

(1)hashCode() 的作用是获取哈希码:

也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。

(2)散列表存储的是键值对(key-value):

它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

(3)使用场景:

HashCode 是为了集合操作快速,而根据一定规则而设计的散列码(根据内存地址生成的,为了方便比较和插入数据),用于 HashMap,HashSet,HashTable.

(4)哈希算法也称为散列算法:

是将数据依特定算法直接指定到一个地址上。hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)

2、源码:

public native int hashCode();

3、HashSet 如何检查重复:

我们都知道HashSet里面元素是不能重复的,下面主要说下当hashset中插入新元素时HashSet 如何检查重复元素的:
(1)当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置;
这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度
(2)总结:当集合要添加新的元素时,
先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次

4、hashCode()与equals()的关系:

(1)如果两个对象相等,则hashcode一定也是相同的

(2)两个对象相等,对两个对象分别调用equals方法都返回true

(3)两个对象有相同的hashcode值,它们也不一定是相等的
因此,重写equals时必须重写hashCode方法,若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值;
equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

5、为什么覆盖equals时总要覆盖hashCode:

一个很常见的错误根源在于没有覆盖hashCode方法。在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap、HashSet和Hashtable。
(1)在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。
(2)如果两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
(3)如果两个对象根据equals()方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生相同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能

6、往hashmap插入数据的流程:

先 用HashCode 找到相应的桶(HashMap是数组+链表,HashCode负责在数组中找相应的HashCode的位置),然后 用equals 在 刚才找到的相同的HashCode堆中,是个HashCode值相同的链表,在这个链表中用equals 检查里面的对象是不是有跟插入的值,相同的,有就舍弃,没有就插入。(注意:在HashSet中插入同一个元素(hashCode和equals均相等)时,会被舍弃,而在HashMap中插入同一个Key(Value 不同)时,原来的元素会被覆盖

hashset源码:

public V put(K key, V value) {  
        if (key == null)  
            return putForNullKey(value);  
        int hash = hash(key.hashCode());  
        int i = indexFor(hash, table.length);  
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
            Object k;  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
                V oldValue = e.value;  
                e.value = value;  
                e.recordAccess(this);  
                return oldValue;  
            }  
        }  
  
        modCount++;  
        addEntry(hash, key, value, i);  
        return null;  
    }

四、自动装箱与拆箱

1、定义:

装箱:将基本类型用它们对应的引用类型包装起来;

拆箱:将包装类型转换为基本数据类型;

2、使用:

int 和 Integer 区别:
Java 为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型,比如int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换

Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

3、高频易错实例:

上面已经讲到,
对于对象引用类型:==比较的是对象的内存地址。
对于基本数据类型:==比较的是值
注意点:如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a == b的结果是false

public static void main(String[] args) {
    Integer a = new Integer(1);
    Integer b = 1;  // 将1自动装箱成Integer类型
    int c = 1;
    System.out.println(a == b); // false 两个引用没有引用同一对象
    System.out.println(a == c); // true a自动拆箱成int类型再和c比较
    System.out.println(b == c); // true

    Integer a2 = 127;
    Integer b2 = 127;
    System.out.println(a2 == b2); // true ,在-128到127之间
    
	Integer a1 = 128;
    Integer b1 = 128;
    System.out.println(a1 == b1); // false,不在-128到127之间
}

五、总结:

1、equals方法用于比较对象的内容是否相等(覆盖以后)
2、hashcode方法只有在集合中用到
3、当覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)。
4、将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入
5、对于int和integer比较,如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值