学习笔记------- ==   equals   hashcode

先来回顾一下 == 与equals 的区别

==是运算符

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

1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean ,他们之间的比较,应该用双等号(==),比较的是他们的值。

2.引用数据类型,比如字符串,或者一个类的对象   当他们用(==)进行比较的时候,实际上比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。

equals是方法

JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址的(等同于“==”)

Object类中:

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

但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,比如String类中

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
        char v1[] = value;
        char v2[] = anotherString.value;
        int i = offset;
        int j = anotherString.offset;
        while (n-- != 0) {
            if (v1[i++] != v2[j++])
            return false;
        }
        return true;
        }
    }
    return false;
    }

因此,此时的equals不再是比较类在堆内存中的存放地址了。

对于引用类型数据之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的。

测试如下:

那么hashcode()是什么?

同equals(Object obj)一样,在Java中任何一个对象都具备hashcode()这个方法,因为他也是在Object类中定义的。 不同的是equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。

而hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。

关于这两个方法有一些必须要遵循的规范(最重要的两个):

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

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

总结如下:

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

2、如果两个对象不equals,他们的hashcode有可能相等。

3、如果两个对象hashcode相等,他们不一定equals。

4、如果两个对象hashcode不相等,他们一定不equals。

上面我们已经知道String类重写了equals方法,那它有没有重写hashcode方法呢?

Object中的hashcode方法:

public native int hashCode();

对,你没有看错object中并没有实现hashcode  他是一个native方法,只在联合开发中用到,我们只用知道底层会返回给我们对应的int值即可。

而在String类中

public int hashCode() {
       int h = hash;
       if (h == 0 && value.length > 0) {
           char val[] = value;
 
           for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
           }
           hash = h;
       }
       return h;
}

可以看到String已经将hashcode重写。测试一下

当然hashcode相当 不一定equals

从上可知,不同的对象,hashcode()可能相等的,这也就有了hash表的冲突解决问题,有了对象相等比较时的equals()问题了。

重写equals方法后为什么要重写hashcode方法,遵从这个规范会给我们带来哪些好处?

重写equals方法时需要重写hashCode方法,主要是针对MapSet等集合类型的使用

我们先来了解一下hashcode的作用。

HashCode有什么用?不妨举个例子:

1、假设内存中有0 1 2 3 4 5 6 7 8这8个位置,如果我有个字段叫做ID,那么我要把这个字段存放在以上8个位置之一,如果不用HashCode而任意存放,那么当查找时就需要到8个位置中去挨个查找

2、使用HashCode则效率会快很多,把ID的HashCode%8,然后把ID存放在取得余数的那个位置,然后每次查找该类的时候都可以通过ID的HashCode%8求余数直接找到存放的位置了

3、如果ID的HashCode%8算出来的位置上本身已经有数据了怎么办?这就取决于算法的实现了,比如ThreadLocal中的做法就是从算出来的位置向后查找第一个为空的位置,放置数据;HashMap的做法就是通过链式结构连起来。反正,只要保证放的时候和取的时候的算法一致就行了。

4、如果ID的HashCode%8相等怎么办(这种对应的是第三点说的链式结构的场景)?这时候就需要定义equals了。先通过HashCode%8来判断类在哪一个位置,再通过equals来在这个位置上寻找需要的类。对比两个类的时候也差不多,先通过HashCode比较,假如HashCode相等再判断equals。如果两个类的HashCode都不相同,那么这两个类必定是不同的。

举个实际的例子Set。我们知道Set里面的元素是不可以重复的,那么如何做到?Set是根据equals()方法来判断两个元素是否相等的。比方说Set里面已经有1000个元素了,那么第1001个元素进来的时候,最多可能调用1000次equals方法,如果equals方法写得复杂,对比的东西特别多,那么效率会大大降低。使用HashCode就不一样了,比方说HashSet,底层是基于HashMap实现的,先通过HashCode取一个模,这样一下子就固定到某个位置了,如果这个位置上没有元素,那么就可以肯定HashSet中必定没有和新添加的元素equals的元素,就可以直接存放了,都不需要比较;如果这个位置上有元素了,逐一比较,比较的时候先比较HashCode,HashCode都不同接下去都不用比了,肯定不一样,HashCode相等,再equals比较,没有相同的元素就存,有相同的元素就不存。如果原来的Set里面有相同的元素,只要HashCode的生成方式定义得好(不重复),不管Set里面原来有多少元素,只需要执行一次的equals就可以了。这样一来,实际调用equals方法的次数大大降低,提高了效率。

简单来说,一般用java中的Map对象进行存储时,他会先自动调用hashCode方法来比较两个对象是否相等,如果相等再继续根据equals方法判断是否为同一对象。

所以如果我们对equals方法进行了重写,建议一定要对hashCode方法重写,以保证相同的对象返回相同的hash值,不同的对象返回不同的hash值。

举个hashset的小例子:

运行结果:

第一部分是new出来的两个对象,它全加了进去。

第二部分,对象是相同的只加一个。

第三部分是new出来的两个对象,但String重写了hashcode和equals方法,断定为相同。

第一部分中,Persion类是Object的子类继承了Object的equals和hashcode方法,而在Object类当中它的不同的实例中的hashcode是不同的,这是Object中本来就定义好了的hashcode;所以继承它的类也都有这样的方法,所以第一部分通过equals之后Persion的两个实例是不相同的

第一部分钟如果重写equals方法,不重写hashcode方法有什么结果呢?

加入我们定义名字相同的人就是同一个人,然后重写equals方法,不重写hashcode

public boolean equals(Object obj) {
       if(this==obj)
       {
           return true;
       }
       if(obj!=null&&obj instanceof Person)
       {
           Person p = (Person)obj;
           if(name.equals(p.name))
           {
                return true;
           }
       }
       return false;
}

测试

定义好名字相同即为一人,在加入到set里时却还是认为是两个不同的人。

所以重写equals方法时也要重写hashcode方法。

时间: 2024-08-06 07:50:15

学习笔记------- ==   equals   hashcode的相关文章

java 笔记 == , equals , hashcode()的区别

== , equals , hashcode()的区别: 基本数据类型:比较用==, 比较他们的值 复合数据类型:用==比较时,比较的是它们在内存中存放的地址,除非是同一个new出来的对象,他们的比较后果为true,否则为false. object基类中定义了equals()方法,它的初始行为是比较它们的内存地址(就是和==功能一样),但在有些类库中覆盖了,比如String类的equals()方法是比较它们的内容.equals()方法在object类中定义如下:   public boolean

equals(),hashcode(),克隆学习心得

equals(),hashcode(),克隆学习心得 其实在开发时候,很少去重写equals(),hashCode()方法,但是有些时候业务需要还是要重写. 注意: 重写equals()方法一定要重写hashCode()方法. notes: java中两个对象的比较,首先查看的就是一个对象的hashCode,可以把hashCode理解为索引,通过索引可以找到其对应下的内容,可能会有多个. 如果说两个对象的hashCode都不相等,那可以肯定这个对象不同. equals() 相等的两个对象,其ha

Java集合框架学习笔记

本文为学习笔记,学习课程为慕课网Java入门第三季中的集合框架部分,若需要研究文中的代码,可前往下载.http://www.imooc.com/learn/110 1. List(Collection子接口) 1.1 实例化 List list = new ArrayList(); ??List是一个接口,不可直接实例化,通常情况下ArrayList实现类进行实例化. 1.2 增 1.2.1 add(obj) ??直接将obj对象加入List末位. 1.2.2 add(i, obj) ??将ob

Java Object类学习笔记

看下Api文档的一些说明 public class Object Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class. Since: JDK1.0 从JDK1.0就已经存在的元老类,类结构的根,所有类的父类,所有类都实现了这个类的方法,包含

JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue

前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的文章,大多都是以罗列记忆点的形式书写的,没有谈论实现细节和逻辑原理.作为个人笔记无可厚非,但是并不利于他人学习.希望能通过这种比较“费劲”的讲解,帮助我自己.也帮助读者们更好地学习Java.掌握Java. 无论你跟我一样需要应聘,还是说在校学生学习Java基础,都对入门和进一步启发学习有所帮助.(关

Java 学习笔记(2015.7.13~17)

Java 学习笔记(2015.7.13~17) Java this关键字 表示本类中的属性,调用本类中的方法 class Person {        private String name;         private int age;         public Person(String name, int age) {         this.name = name;//调用本类中的属性         this.age = age;//同上} //get&set方法:    

集合类学习笔记

一.概念 集合是存储对象的一种方式.集合中都是存放着地址,方便引用.JDK 1.2版本的时候就有了 二.集合和数组的区别 集合是可变长度,数组是固定长度. 数组可以存储基本数据类型,集合只能存储对象,集合可以存储不同类型的对象. Collection 1.List:元素是有序的,可以重复,有索引 2.Set:元素是无序的,不可以重复,使用hash值排列 三.CURD boolean add(E e); boolean addAll(Collection<? extends E> c); voi

学习笔记---使用override注解

在学习或者做东西,都懒得动手记,得习惯记下来才好... 最近在看<Effective Java>,确实对我有很大的提高,把自己看到和想到的东西写下来,标记... 1. 坚持使用override注解. 1 public class Bigram { 2 3 private final char first; 4 private final char second; 5 public Bigram(char first,char second) { 6 this.first = first; 7

学习笔记 07 --- JUC集合

学习笔记 07 --- JUC集合 在讲JUC集合之前我们先总结一下Java的集合框架,主要包含Collection集合和Map类.Collection集合又能够划分为LIst和Set. 1. List的实现类主要有: LinkedList, ArrayList, Vector, Stack. (01) LinkedList是双向链表实现的双端队列:它不是线程安全的.仅仅适用于单线程. (02) ArrayList是数组实现的队列,它是一个动态数组.它也不是线程安全的,仅仅适用于单线程. (03