Java hashCode

来源:互联网 发布:python 矩阵转图片 编辑:程序博客网 时间:2024/06/01 08:45

 Java 使用hashCode意在提高查询效率。

    首先java在比较2个对象是否相等的时候会比较hashCode,如果hashCode相等 ,会再去比较对象是否equals;如果hashCode不等就直接判定2个对象不等。(这里有个原则:如果2个对象equals 则hashCode一定相等,如果hashCode相等 却不一定equals)
public class HashCodeTest {
    public static void main(String[] args) {
        Person p1 = new Person(000, "lisi");
        Person p2 = new Person(001, "lisi");
        Person p3 = new Person(002, "zhangshan");

        System.out.println(p1.hashCode());//p1与p2的hashCode相等
        System.out.println(p2.hashCode());
        System.out.println(p3.hashCode());

        System.out.println(p1.equals(p2));//p1与p2不相等
        System.out.println(p1.equals(p3));
    }
}

class Person{
    int id;
    String name;
    public Person(int pId,String pName){
        this.id = pId;
        this.name = pName;
    }
    
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
    
    @Override
    public String toString() {
        return this.id + " : " + this.name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (id != other.id)
            return false;
        if(name != other.name)
            return false;
        return true;
    }
}

    equals与hashCode方法都来自于Object类,而Object类的hashCode和equals都是比较2个对象是否是同一个对象 (即比较的是==)。必需清楚 String 、Math、Integer、Double等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。


    要比较2个对象是否相等必须重写equals,重写equals又必须重写hashCode方法。这是由于Java在比较调用equals()之前先会调用hashCode。所以 如果只重写equals方法而不重写hashCode方法,在比较时会调用父类(Object)的hashCode方法,这样就失去了写equals的意义。


    之所以在比较之前会使用hashCode,原因就在于这篇文章的标题(提交查询效率)。

以下例子使用了上面的Person类

public static void main(String[] args) {
        Person p1 = new Person(000, "lisi");
        Person p2 = new Person(0001, "lisi");
        Person p3 = new Person(000, "zhangshan");
        Person p4 = new Person(000, "zhangshan");

        Collection<Person> collection = new HashSet<Person>();
        collection.add(p1);
        collection.add(p2);
        collection.add(p3);
        collection.add(p4);

        for (Person p : collection) {
            System.out.println(p);
        }
    }

大家应该都知道,set容器是装的是无序不可重复的元素,所以 collection.add()改动作会去查询当前待加入的对象是否在collection中是否已存在。试想,如果collection中已有很多元素,那么会用equals去一个一个比较collection中的每一个元素,这样其实很蠢、效率很低。所以使用了hashCode解决了次问题。


    HashCode原理浅析:使用hash算法实现的容器会对立面的元素进行分区(按照hashCode值来分的),当在add元素时会对元素计算出一个hashCode码(至于Java是怎么样计算的我们并不关心),然后根据这个码查询应该添加到容器的哪一个区域(这样避免了一个一个大量比较每一个元素带来的时间)。


    HashCode带来的内存泄露问题(只是表面,java中有GC回收)。

public static void main(String[] args) {
        Person p1 = new Person(000, "lisi");
        Person p2 = new Person(001, "lisi");
        Person p3 = new Person(000, "zhangshan");

        Collection<Person> collection = new HashSet<Person>();
        collection.add(p1);
        collection.add(p2);
        collection.add(p3);

        p2.name = "wangwu";
        collection.remove(p2);//p2没有remove成功
        collection.remove(p3);//p3成功remove
        for (Person p : collection) {
            System.out.println(p);
        }
    }

原因在于:当collection已经把p2加入容器后,根据p2的hashCode码在collection的位置已经确定了,而之后改变了p2对象中的name属性,则p2的hashCode码就变了。然后去 collection.remove(p2)时p2的hashCode码在collection中的特定区域内并不存在,所以会认为collection中斌没有p2对象,这就造成了p2没有remove成功的现象。


      注意:只有使用hash算法实现的容器在比较元素时才会使用hashCode方法,如HashSet,HashMap,HashTable.....。故使用Hash的效率较高。



1 0
原创粉丝点击