1.equals()和==操作符
先谈谈equals()方法和==操作符吧。有一个Book类,如下代码:
- public class Book {
- private String name;
- private int price;
- public Book(String name, int price) {
- super();
- this.name = name;
- this.price = price;
- }
- }
当用equal和==比较两个Book的实例对象时,均为false:
- Book book = new Book("java", 11);
- Book book1 = new Book("java", 11);
- System.out.println(book == book1); //false
- System.out.println(book.equals(book1));//false
这是内存布局图:
new出book1和book两个Book的实例对象时,内存布局如上图,book和book1两个变量的值均在栈内存中分配,其存储的是两个book对象的内存地址。
使用==比较两个对象:其实是在比较两个对象的引用的值。
book == book1实际上是在比较0X00F1 == 0X00F2是否相等,答案肯定是不等的。用==在比较时,有如下规则:
1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
他们之间的比较,应用双等号(==),比较的是他们的值。
2.复合数据类型(类)
当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。
使用equals比较两个对象:结果也为false,equals方法继承自Object类,其内容如下:
- public boolean equals(Object obj) {
- urn (this == obj);
- }
要重写equals方法必须遵守重写规范,如果不遵守,则会导致很多错误,
下面是约定的内容,来自java.lang.Object的规范,equals方法实现了等价关系,以下是要求遵循的5点
1. 自反性:对于任意的引用值x,x.equals(x)一定为true。
2. 对称性:对于任意的引用值x 和 y,当x.equals(y)返回true时,y.equals(x)也一定返回true。
3. 传递性:对于任意的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也一定返回true。
4. 一致性:对于任意的引用值x 和 y,如果用于equals比较的对象信息没有被修改,多次调用x.equals(y)要么一致地返回true,要么一致地返回false。
5. 非空性:对于任意的非空引用值x,x.equals(null)一定返回false。
重写equals方法后最好重写hashCode方法,否则两个等价对象可能得到不同的hashCode,这在集合框架中使用可能产生严重后果。所以除了要遵守以上五点以外,还要同时重写hascode方法。
2.hashcode方法
定义:public native int hashCode();
说明是一个本地方法,它的实现是根据本地机器相关的。再看它比较“官方”的详细说明:hashCode()返回该对象的哈希码值,该值通常是一个由该对象的内部地址转换而来的整数,它的实现主要是为了提高哈希表(例如java.util.Hashtable提供的哈希表)的性能。
在每个重写了equals方法的类中,你必须也要重写hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法与所有基于散列值(hash)的集合类结合在一起正常运行。
hashCode()的返回值和equals()的关系如下:
如果x.equals(y)返回“true”,那么x和y的hashCode()必须相等。
如果x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。
3.重写equals()和hashcode()
为了使对象能够在逻辑上相等时equals方法能够返回true,我们来重写equals方法。
- @Override
- public boolean equals(Object obj) {
- //1. 自反性:对于任意的引用值x,x.equals(x)一定为true。
- if (this == obj)
- return true;
- //如果目标对象为空,则为false
- if (obj == null)
- return false;
- //如果两个对象不属于同一个类,则返回true
- if (getClass() != obj.getClass())
- return false;
- //将Object强转为Book
- Book other = (Book) obj;
- //对Book的两个属性进行比较,如果都完全相等,则返回true.
- if (name == null) {
- if (other.name != null)
- return false;
- } else if (!name.equals(other.name))
- return false;
- if (price != other.price)
- return false;
- return true;
- }
同时要重写hashcode方法:
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- result = prime * result + price;
- return result;
- }
在HashSet中测试重写的正确性:
- Book book = new Book("java", 11);
- Book book1 = new Book("java", 11);
- HashSet<Book> set = new HashSet<Book>();
- set.add(book);
- set.add(book1);
- System.out.println(set.size());
HashSet中的存储时,会首先判断两个对象的hashcode值是否相等, 如果不相等,直接判断为不同的对象,就添加进去了,如果相等,再用equals方法进一步判断两个对象是否相等。
如没有重写equals方法和hashcode方法,以上代码的返回值是2,因为两个对象equals不等,所以HashSet把这两个对象当做不同的对象来处理,都会添加进去。
如果重写了equals方法而没有重写hashcode方法,返回值也是2,因为他们的hashcode方法可能不相等,HashSet直接就认为这两个对象不等,不进行equals判断了。
只有重写了equals方法和hashcode方法,返回值才为1,HashSet先进行hashcode判断,再进行equals判断,发现两个对象相等,认为这两个对象相等,就不会添加了。
特别注意:如果重写了这两个方法,在往HashSet中添加完值以后,就不能修改参与hash运算的字段了,因为一旦修改,由于HashSet是根据hash值来存储和获取对象的,我们将获取不到这个对象,会造成内存泄露。
转载自:http://blog.csdn.net/qq525201608/article/details/21238079