一、对于包装类型的比较,使用的是equals方法 而不是==
1、首先equals是Object中的方法,Object中equals方法是怎么实现的呢。
public boolean equals(Object obj) { return (this == obj); }
2、但是对于对象来说,==比较的是对象在内存中的地址,而不是值的大小,为了能比较对应的值的大小,包装类重写了
Object中的equals方法,使之比较对应的值的大小。
案例:
public class BackTrace { public static void main(String[] args) { Object obj = new Object(); obj.equals(obj); new BackTrace().f(); } public void f(){ Integer i1 = new Integer(1); Integer i2 = new Integer(1); Double d = new Double(1.00); if (i1 == i2) { System.out.println("A"); } if (i1.equals(i2)) { System.out.println("B"); } if (i1.equals(d)) { System.out.println("C"); } } }
我们可以看一下打印的结果是什么
为什么没有打印C呢?我们来看一下源码
public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
在源码中,我们会首先进行判断传进来的Obj是否属于Integer,如果不属于Integer则直接返回false,所以我们会直接返回false而不会输出C。
再接下来,我们看一下输出B,源码中给出的是intValue() 而不是比较的地址,所以会输出B
二、对自定义类型如何比较
1、肯定不能用==符号来比较
2、重写equals方法,自定义比较规则,如果一个人的姓名和年龄是相同的,那么就认为同一个人
3、如果重写equals方法,则必须重写hashCode方法(为什么?)
public class Person { //姓名、年龄、地址 private String name; private int age; private String address; public Person(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } public void f2(){ Person p1 = new Person("张三",10,"北京"); Person p2 = new Person("张三",10,"北京"); if (p1.equals(p2)) { System.out.println("Hello World"); } } public static void main(String[] args) { new BackTrace().f2(); }
在这个方法中,我们并没有重写Person中的equals和hashCode,调用这个方法,发现并没有输入Hello World这句话。
我们现在重写equals和hashCode
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name) && Objects.equals(address, person.address); } @Override public int hashCode() { return Objects.hash(name, age, address); }
结果,我们就发现输出了。
总结:
1、equals 是 Object 中的方法,实现方式还是==
2、各个包装类都重写了equals方法,来实现自己的比较逻辑,String类也重写了equals方法。
3、自定义的类比较也要重写equals方法
4、重写equals方法必须重写hashCode;
三、为什么重写equals方法必须重写hashCode;
1、hashCode也是Object中的方法,看源码如何实现的。
2、native关键字说明这个方法是原生函数,也就是这个方法是用c/c++语言实现的,并且被编译成了DLL,
由Java去调用,这些函数的实现体在DLL中,JDK源代码中并不包含,你应该是看不到的。
一般情况下,对象不同,hashCode也就不同
下面的是特殊情况:
System.out.println("Aa".hashCode()); System.out.println("BB".hashCode()); 2112 2112
已经重写了equals,当姓名和年龄相同的时候,就认为他们是一个人。
这里的p1和p2就是同一个人,那么我们用Map去存储他的分数(没有重写hashCode),正常情况下,我们期望返回95。
我们现在将Person中的hashCode去掉,然后用HashMap去存储和获取。
public static void main(String[] args) { //Person为键,String为值 HashMapmap = new HashMap<>(); map.put(new Person("张三",10,"北京"),"95"); String s = map.get(new Person("张三", 10, "北京")); System.out.println(s); }
会发现返回的是Null
所以并没有通过张三这个对象拿到对应的分数,如果将这个hashCode解开会发现拿到了这个分数。
HashMap的存储方式
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
HashMap的存储方式,使用key的hash值 作为Key来存储对应的值。
如果我们没有重写hashCode的时候,我们会用Object自带的hashCode去存储,如果重写了hashCode,我们会以年龄和姓名进行获取返回相同的hash值。
3、HashMap哈HashSet的存取都是这样实现的。
4、发生了hash碰撞怎么办,调用equals方法
5、HashMap的存储结构时怎么样的?
对于equals和hashCode,Object规范:
1、在应用程序执行期间,只要对象的equals方法的比较 *** 作所用到的信息没有被修改,那么对同一个对象的多次调用,hashCode方法都必须始终返回同一个值。
(对于Person中的姓名和年龄等属性相同,那么hashCode返回同一个值)
2、如果两个对象根据equals方法比较是相等的,那么调用这两个对象的hashCode方法都必须产生同样的整数结果
3、如果两个对象根据equals方法比较是不相等的,那么调用这两个对象中的hashCode的方法则不一定要求hashCode方法必须产生不同的结果。
但是开发人员应该知道,给不相等的对象产生不同的整数结果,有可能提高散列表的性能。
总结:
1、HashCode也是Object中的方法
2、Native关键字说明这个方法是原生函数,也就是这个方法是用c/c++语言实现的,我们看不到源码的实现。
3、一般情况下,对象不同(equals不同),hashCode一般不同,但是对象相同(equals相同),hashCode一定相同。
4、HashMap和HashSet的存取都是通过key的hash值来存储的
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)