在我们平常的学习中,经常会涉及到equals、hashCode、“==”这三者,对于这三者我经常混淆不清。因此,我自行总结了一下。
先说下在我们编程中用到的比较不容易区分的equals和hashCode的区别
equals和hashCode都是Object类中的方法,源码中是这样解释的。
可跳过源码,接着往下看~~~
public native int hashCode();
/**
* Indicates whether some other object is "equal to" this one.
* <p>
* The {@code equals} method implements an equivalence relation
* on non-null object references:
* <ul>
* <li>It is <i>reflexive</i>: for any non-null reference value
* {@code x}, {@code x.equals(x)} should return
* {@code true}.
* <li>It is <i>symmetric</i>: for any non-null reference values
* {@code x} and {@code y}, {@code x.equals(y)}
* should return {@code true} if and only if
* {@code y.equals(x)} returns {@code true}.
* <li>It is <i>transitive</i>: for any non-null reference values
* {@code x}, {@code y}, and {@code z}, if
* {@code x.equals(y)} returns {@code true} and
* {@code y.equals(z)} returns {@code true}, then
* {@code x.equals(z)} should return {@code true}.
* <li>It is <i>consistent</i>: for any non-null reference values
* {@code x} and {@code y}, multiple invocations of
* {@code x.equals(y)} consistently return {@code true}
* or consistently return {@code false}, provided no
* information used in {@code equals} comparisons on the
* objects is modified.
* <li>For any non-null reference value {@code x},
* {@code x.equals(null)} should return {@code false}.
* </ul>
* <p>
* The {@code equals} method for class {@code Object} implements
* the most discriminating possible equivalence relation on objects;
* that is, for any non-null reference values {@code x} and
* {@code y}, this method returns {@code true} if and only
* if {@code x} and {@code y} refer to the same object
* ({@code x == y} has the value {@code true}).
* <p>
* Note that it is generally necessary to override the {@code hashCode}
* method whenever this method is overridden, so as to maintain the
* general contract for the {@code hashCode} method, which states
* that equal objects must have equal hash codes.
*
* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
* @see #hashCode()
* @see java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
}
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object {@code x}, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be {@code true}, this is not an absolute requirement.
* <p>
* By convention, the returned object should be obtained by calling
* {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
* <p>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by {@code super.clone}
* need to be modified.
* <p>
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
这两者常出现在散列的数据结构中(HashSet、HashMap、LinkedHashSet或LinkedHashMap),在散列的数据结构寻址中就用到这两个方法(后期会更新Map集合和HashMap的使用)。对于这篇文章主要来谈一下,equals和hasdCode的区别,“==”作为附带品。
在散列数据结构HashMap中寻址遵循的原则是:
当equals()相等时,hascode()必定相等。
相反hascode()相等时,equals()不一定相等。
只有当equals和hascode同时相等时才能判断到两个值相等。
再继续的深入了解一下:
1.关于equals()方法(来自于Java编程思想):
正确使用equals()方法必须满足下列5个条件:
(1)、自反性。对于任意x,x.equals(x)一定返回true
(2)、对称性。对任意x和y,如果y.equals(x)返回true,则x.equals(y)也返回true。
(3)、传递性。对任意x、y、z,如果有x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)一定返回true。
(4)、一致性。对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应该保持一致,要么一直是true,要么一直是false。
(5)、对于任何不是null的x,x.equals(null)一定返回false。
在Object类中的equals()方法比较的是对象的地址。(此处的地址所指的是磁盘的逻辑地址并非物理地址,对于逻辑地址与物理地址的概念可以查询操作系统相关书籍,在操作系统中,一般都不会出现真正的物理地址)
2. hashCode()方法在计算机科学的术语中称为散列函数。
什么是hashCode?
hashCode是jdk根据对象的地址 或者字符串或者数字算 出来的int类型的数值
散列的价值:
散列使得查询得以快速进行。在Map集合中,对于键的存储没有按照任何的顺序保存,所以要查询的话是简单的线性查询,而线性查询又是最慢的查询方式。
散列的作用使得键保存在某处,以便能很快的找到。而存储一组元素最快的数据结构是数组(数组不能调整容量),所以用数组保存键的信息。
问题:我们希望在Map中保存数量不确定的值,但是如果键的数量被数组的容量限制了,该怎么办?
答:在数组中保存的不是键的本身,而是通过键对象生成一个数字,将其作为数组的下标,这个数字就是散列码,由定义在Object中的、且可能由你的类覆盖的hashCode()方法生成。
关于equals方法,一致的约定是:
-
重写了euqls方法的对象必须同时重写hashCode()方法。
-
如果2个对象通过equals调用后返回是true,那么这个2个对象的hashCode方法也必须返回同样的int型散列码
-
如果2个对象通过equals返回false,他们的hashCode返回的值允许相同。
也就是说参与equals方法的字段,都必须参与hashCode字段的运算。
关于hashCode方法,一致的约定是:
-
在某个运行时期间,只要对象的(字段的)变化不会影响equals方法的决策结果,那么,在这个期间,无论调用多少次hashCode,都必须返回同一个散列码。
-
通过equals调用返回true 的2个对象的hashCode一定一样。
-
通过equasl返回false 的2个对象的散列码不需要不同,也就是他们的hashCode方法的返回值允许出现相同的情况。
总结:等价的(调用equals返回true)对象必须产生相同的散列码。不等价的对象,不要求产生的散列码不相同。
在使用散列的数据结构中,如果不为你的键覆盖hashCode()和equals(),那么使用散列的数据结构中就无法正确的处理键。
“==”和equals的区别!!!
“ == ” 号在比较基本数据类型时比较的是值,而用“ == ”号比较两个对象时比较的是两个对象的地址值。
equals方法通过源码我们发现,Object类中equals()方法底层依赖的是“ == ”号,那么,在所有没有重写equals()方法的类中,调用equals()方法其实和使用“ == ”号的效果一样,也是比较的地址值,然而,Java提供的所有类中,绝大多数类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值。
那么怎么去深层次一点去区分他们呢,那就继续往下看。
对于“ == ”和equals方法的比较我们通常在引用数据类型中讨论(基本数据类型都在堆上存储,不用去比较两者的不同)
基本数据类型和引用数据类型:
这里简单提一下,不做太多的讨论(可自行查阅一下)。
- Java中基本数据类型分为8种boolean、byte、short、int、long、float、double、char,存储方式在内存的堆上存储。
- 引用数据类型是在栈上开辟空间,指向堆
基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的
来道题分析:
String s = new String("hello");
String s1 = "hello";
System.out.println(s == s1);
System.out.println(s.equals(s1));
编译的结果为:
false
true
分析:对于==比较的是引用数据类型的引用是否相等,也就是所指向的内存地址是否一样,这里我们知道s所指向的内存地址是在堆内存中的,而s1是在字符串常量池中的,虽然指向的字符串内容都是“hello”,可是地址是不同的。
小结:对于equals就是比较引用地址所指向的内存地址锁对应的内容是否一致的,也就是说==比较内存地址,而equals比较内存地址指向的内容是否一样!
- 注意:默认的equals方法也是使用了==,所以一般这个equals是由我们去覆写的,如果不覆写,那其实跟==没什么区别。
综上,是我自己对这三者的总结,本人学编程一段时间,但还是菜鸟一枚,故打算好好扎实基础,欢迎读者找出文章的问题与不足之处,期望一块努力学习编程,一块成长。