- 浏览: 498208 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
herofighter2008:
图呢?图呢?图呢?图呢?
BlockingQueue -
zy13608089849:
请问一下博主,文中几处提到的图,怎么都没有?是我这显示不出来还 ...
BlockingQueue -
swift911:
在短信的场景下很好用,感谢分享
BlockingQueue -
tony_0529:
学习了~谢谢分享。
BlockingQueue -
Master-Gao:
...
BlockingQueue
JDK的java.lang.Object类中实现了equals函数,其定义说明如下:
Indicates whether some other object is "equal to" this one.
The equals method implements an equivalence relation on non-null object references:
[list=5]
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
[/list]
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
其具体的实现代码如下所示:
return (this == obj);
}
从上面的代码中可以看出,Object类的equals实现只是简单地调用了“==”运算符,也即定义中说明的,对于两个非空对象X和Y,有且仅当X和Y
指向同一个对象时该函数才返回true。由于Object类是java中所有类的超类,因而其它任何类都默认继承了此函数。在大多数情况下,此默认的实现
方式是合适的,因为任一个类本质上都是唯一的;但是对于那些表示“值”的类(如Integer、String等),它们引入了自己特有的“逻辑相等”的概
念,此时就必须修改equals函数的实现。
例如java.lang.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; }
而java.lang.Integer类的实现如下:
- public boolean equals(Object obj) {
- if (obj instanceof Integer) {
- return value == ((Integer)obj).intValue();
- }
- return false;
- }
String类的equals实现使得当两个String对象所表示的字符串的值是一样的时候就能返回true;而Integer类则当两个Integer对象所持有的整数值是一样的时候就能返回true。
因此,当我们我们自己编写的类需要引入“逻辑相等”的概念,且其超类又没有改写equals的实现时,我们就必须实现自己的equals函数。在上面Object类中关于equals函数的说明中,定义了5条改写equals函数所必须遵守的规范。其中第1条自反性(reflexivity)和第5条通常能够自行满足。下面我们重点介绍下第2点对称性(symmetric)、第3点传递性(transitive)和第4点一致性(consistent)。
对称性
即对于任意两个引用值X和Y,X.equals(Y) 和 Y.equals(X) 返回的结果必须是一致的。下面引用《effect java》上的例子来说明了违反这一特性的后果:
- public class CaseInsensitiveString {
- private String s;
- public boolean equals(Object obj){
- if(obj instanceof CaseInsensitiveString)
- return s.equalsIgnoreCase(((CaseInsensitiveString)obj).s);
- if(obj instanceof String)
- return s.equalsIgnoreCase((String)obj);
- return false;
- }
- }
然后我们定义如下的类来进行测试:
- public void test(){
- CaseInsensitiveString cis = new CaseInsensitiveString("Value");
- String s = "Value";
- List list = new ArrayList();
- list.add(cis);
- System.out.println(cis.equals(s));
- System.out.println(s.equals(cis));
- System.out.println(list.contains(s));
- }
运行test(),第一条语句的输出为true,而后两条为false。因为CaseInsensitiveString类中equals函数对String对象的比较也可能返回true,只要其大小写不敏感的字符串值相等;而在String类中对任何非String对象都返回false,这样CaseInsensitiveString的实现就违反了对称性,从而导致了第三条语句也返回了意想不到的结果false。这是因为ArrayList的contains函数实现是用实参s对list中每一个对象调用equals函数,若有equals函数返回true,则contains返回true;因而在这里contains函数必然返回false。因此,如果违反了对称性,则可能会得到意料之外的结果。解决此问题的一个办法就是在CaseInsensitiveString类的equals函数中对Stirng对象的比较无论值是否一致都返回false。
传递性
即对于任意的引用值X、Y和Z,如果 X.equals(Y) 和 Y.equals(Z) 都返回true,则 X.equals(Z) 也一定返回true。下面举例说明:
- public class Point {
- private final int x;
- private final int y;
- public Point(int x, int y) {
- this.x = x;
- this.y = y;
- }
- public boolean equals(Object obj) {
- if (!(obj instanceof Point))
- return false;
- Point p = (Point) obj;
- return p.x == x && p.y == y;
- }
- }
- public class ColorPoint extends Point {
- private int z;
- public ColorPoint(int x, int y, int z) {
- super(x, y);
- this.z = z;
- }
- public boolean equals(Object obj) {
- if (!(obj instanceof Point))
- return false;
- if(obj instanceof ColorPoint){
- ColorPoint cp = (ColorPoint) obj;
- return super.equals(obj) && cp.z == this.z;
- }else{
- Point cp = (Point) obj;
- return super.equals(obj);
- }
- }
- }
在上面的代码中定义了类Point及其子类ColorPoint,并分别改写了equals函数。下面运行如下代码来测试:
- ColorPoint cp1 = new ColorPoint(1,2,3);
- ColorPoint cp2 = new ColorPoint(1,2,4);
- Point p = new Point(1,2);
- System.out.println(cp1.equals(p));
- System.out.println(p.equals(cp2));
- System.out.println(cp1.equals(cp2));
结果前两条语句输出“true”,而最后一条语句输出为“false”;从而违反了传递性规范。其主要原因是在基类的equals函数中并未对子类加以区分,从而使得基类与子类之间的比较也能返回true。要解决此方法,《effect java》上提供了“复合代替继承”的方法;另外,如果可以将基类和子类视为不等的话,使用 getClass()函数代替 instanceof 运算符进行比较可以很好的解决这一问题。getClass()会返回此对象的运行期类(runtime class),因为基类和子类属于不同的class,getClass()能够将其区分开来。下面是一段示例的代码:
- public boolean equals(Object obj) {
- if(obj.getClass() != this.getClass())
- return false;
- Point p = (Point) obj;
- return p.x == x && p.y == y;
- }
一致性
即如果两个对象相等的话,那么它们必须始终保持相等,除非至少有一个对象被修改了。
在Object类equals函数的说明中的最后一段提到当我们改写equals函数的时候,通常都需要改写hashCode函数,后者同样在Object类中进行了定义,如下:
public int hashCode()
Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.
The general contract of hashCode is:
[list=3]
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
hashCode()函数返回一个对象的散列值(hash code),在java中有些集合类都是基于散列值的,如HashMap、HashSet、Hashtable等;它们都根据对象的散列值将其映射到相应的散列桶中。如Hashtable的put和get函数的实现如下所示:
- public synchronized V get(Object key) {
- Entry tab[] = table;
- int hash = key.hashCode();
- int index = (hash & 0x7FFFFFFF) % tab.length;
- for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
- if ((e.hash == hash) && e.key.equals(key)) {
- return e.value;
- }
- }
- return null;
- }
- ublic synchronized V put(K key, V value) {
- // Make sure the value is not null
- if (value == null) {
- throw new NullPointerException();
- }
- // Makes sure the key is not already in the hashtable.
- Entry tab[] = table;
- int hash = key.hashCode();
- int index = (hash & 0x7FFFFFFF) % tab.length;
- for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
- if ((e.hash == hash) && e.key.equals(key)) {
- V old = e.value;
- e.value = value;
- return old;
- }
- }
- modCount++;
- if (count >= threshold) {
- // Rehash the table if the threshold is exceeded
- rehash();
- tab = table;
- index = (hash & 0x7FFFFFFF) % tab.length;
- }
- // Creates the new entry.
- Entry<K,V> e = tab[index];
- tab[index] = new Entry<K,V>(hash, key, value, e);
- count++;
- return null;
- }
因而hashCode函数极大地影响了这些集合类的正常工作。如果在改写完equals函数后,不相应改写hashCode函数,则可能会得不到想要的结果。如下例所示:
- Point p1 = new Point(1, 2);
- Point p2 = new Point(1, 2);
- Hashtable<Point, String> ht = new Hashtable<Point, String>();
- ht.put(p1, "value");
- System.out.println(p1.equals(p2));
- System.out.println(ht.get(p2));
由上面Point类的实现,p1和p2是相等的,第一个语句正常输出true;但是第二个语句输出的值是null,并没有得到期望中的“value”。导致这一问题的根本原因是Point类改写了equals函数,对相等的概念作了更改,但没有相应更改hashCode函数,使得两个相等的函数拥有不同的hashCode,违反了Object类关于hashCode的约定中的第2点,从而返回了错误的结果。
在Object类中定义的几个hashCode约定如下:
1. 在同一应用中,一个对象的hashCode函数在equals函数没有更改的情况下,无论调用多少次,它都必须返回同一个整数。
2. 两个对象如果调用equals函数是相等的话,那么调用hashCode函数一定会返回相同的整数。
3. 两个对象如果调用equals函数是不相等的话,那么调用hashCode函数不要求一定返回不同的整数。
我们在改写equals 和 hashCode 函数的时候,一定要遵守如上3条约定,在改写equals的同时也改写hashCode的实现,这样才能保证得到正确的结果。
发表评论
-
(转)解析WEB开发编码问题
2013-10-17 15:45 1558在技术支持中碰到最 ... -
Java序列化的机制和原理
2012-12-11 13:48 1011原文见http://developer.51cto.com/a ... -
HashMap的实现原理
2012-04-23 15:25 968http://zhangshixi.iteye.com/b ... -
初始化顺序
2012-04-13 11:14 10201、静态变量 2、静态初始化块 3、实例变量 4、实例初 ... -
java方法参数的值传递
2012-04-13 10:54 1043java中方法参数的使用情况: 1、一个方法不能修改一个基本 ... -
native2ascii转码
2012-02-22 17:42 1158背景: 在做Java开发的时候,常常会出现一些乱码, ... -
java中cookie的操作
2012-02-21 14:38 10061.设置Cookie 写道 Cookie ... -
理解cookie和session机制
2012-02-21 14:23 997cookie和session机制之间的区别与联系 ... -
web.xml中的url-pattern写法规则及匹配过程
2012-01-13 15:37 23281. 写法 ①完全匹配:以“/”开头,以字母(非“*”)结 ... -
java对象的强、软、弱和虚引用
2012-01-13 14:48 813本文介绍对象的强、软 ... -
解析Java对象的equals()和hashCode()的使用
2011-11-05 22:08 893前言 在Java语言中,equals()和hashCo ... -
java中的HashCode
2011-11-05 16:51 1040想要明白 hashCode 的作用,你必须要先知道 Java ... -
web.xml详解:元素含义及加载顺序
2011-06-27 20:38 6219web.xml即部署描述符,其包含了很多描述servl ... -
序列化与克隆
2010-09-27 14:26 1128一、序列化 Java 序 ... -
java的异常限制
2010-08-25 14:23 1891在Java中如果某个 ...
相关推荐
在Java编程语言中,`equals()`方法和`hashCode()`方法是两个非常重要的概念,它们主要用于对象的比较和哈希表的高效运作。本解析将深入探讨这两个方法的用途、实现原理以及它们之间的关联。 首先,`equals()`方法是...
`equals()`和`hashCode()`应满足一致性的约定:如果两个对象相等(根据`equals()`),那么它们的哈希码必须相同。 3. **toString()**: 返回一个表示该对象的字符串,通常是类名加上对象的属性值。在调试和打印输出...
在Java中,所有的对象都继承自Object类,而Object类中定义了hashCode()方法和equals()方法。根据Java的约定,如果两个对象通过equals方法比较是相等的,那么这两个对象的hashCode()方法返回的散列码(整数)也应当...
9. 重写 equals 和 hashCode 方法时,需要遵守 equals 方法的约定,hashCode 值相同的两个对象的 equals 方法返回值不一定为 true。 10. 将 Map 集合中的键存储到 Set 集合可以使用 keySet() 方法,entrySet() 方法...
这是基于Object类的equals()和hashCode()方法约定的,如果不遵循这个约定,可能会导致哈希表(如HashMap)的行为异常。 7. **值传递与引用传递**: - 在Java中,所有参数传递都是值传递。当对象作为参数传递时,...
如果希望比较对象的内容,必须在类中实现`equals()`方法,通常同时还需要重写`hashCode()`方法以保持一致性的约定。 例如,假设有一个`Cat`类,没有重写`equals()`方法: ```java class Cat { private String ...
根据Java的约定,如果两个对象通过`equals()`方法判断为相等,那么它们的`hashCode()`必须相同。但是,两个不相等的对象可能具有相同的`hashCode()`,这取决于哈希函数的设计。因此,即使`hashCode()`相同,仍需通过...
根据约定,如果两个对象相等(即 equals() 返回 true),那么它们的 hashCode() 必须相同,但反之不成立。 6. **String 是否为基本数据类型**: 不是,String 是一个对象,属于引用数据类型,它在 Java 中的地位...
6. **hash code一致性**:两个对象值相同(x.equals(y) == true),其hash code也应相同,这是hashCode方法的约定。 7. **对象参数传递**:Java总是进行值传递,但对象引用作为参数时,方法内部可以改变对象的属性,...
- `hashCode()`和`equals()`:定义了对象的哈希码和相等性检查,符合Java对象的一般约定。 - `parseInt(String s)`:解析字符串为指定类型的数值,如果解析失败则抛出NumberFormatException。 10. **自动类型转换...
这是因为`equals`和`hashCode`方法之间有约定:如果两个对象相等(通过`equals`判断),那么它们的哈希码应该相同。这在使用哈希表结构的数据结构(如`HashSet`)时尤为重要。 #### 九、HashMap的工作原理 1. **...
哈希函数是通过键的`hashCode()`方法生成的,它返回一个整数值,然后除以桶的数量取余数,决定键值对在哈希表中的位置。如果多个键的哈希值相同,它们会被放在同一个桶内,此时需要使用`equals()`方法来区分不同的键...
- 根据Java的约定,如果两个对象通过 `equals()` 方法比较结果为 true,那么它们的 `hashCode()` 方法应该返回相同的值。然而,两个具有相同 `hashCode()` 的对象并不意味着它们通过 `equals()` 方法比较会返回 ...
根据`Object`类中`equals()`方法和`hashCode()`方法的约定,如果两个对象通过`equals()`方法比较相等,则它们必须具有相同的哈希码。 **19. String对象是否可变?** 不是,`String`对象是不可变的,即一旦创建之后...
根据给定的文件信息,以下是对“core java达内培训资料”中涉及的关键知识点的深入解析: ### 面向对象(OO) 面向对象编程(OOP)是Java的核心概念之一,它强调通过“对象”来设计和实现软件系统。在Java中,一切皆为...
关于`equals()`和`hashCode()`,Java中的约定是,如果两个对象相等(`equals()`返回`true`),它们的`hashCode()`应当相同,但反之不成立,这是为了解决哈希表的效率问题。最后,`String`类在Java中被设计为不可继承...
- **代码组织**:遵循经典的类设计模式,实现`equals()`、`hashCode()`和`toString()`等基本方法。 - **代码注释**:保持良好的注释习惯,确保代码可读性和可维护性。 - **编码规范**:遵循项目或团队的编码标准,...
【Java基础】 ...以上就是Java面试中常见的一些问题及其解析,涵盖了Java基础、字符串操作、面向对象特性以及I/O流等方面的知识点。理解并掌握这些内容对于提升Java编程技能和应对面试非常有帮助。
以下是对2019年最新Java面试题的详细解答和解析: 1. **JDK和JRE的区别**:JDK(Java Development Kit)是Java开发工具包,包含了编译器、调试器等开发工具以及JRE;JRE(Java Runtime Environment)则是Java运行...
以下是对这些知识点的详细解析: 1. **字符串比较**: - `equals()` 方法用于比较字符串内容是否一致,而`==`则比较的是对象的引用,即内存地址。因此,对于`String`对象,应当使用`equals()`或`Objects.equals()`...