zoukankan      html  css  js  c++  java
  • 关于HashMap自定义key重写hashCode和equals的问题

     使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()

    hashcode()和equals()都继承于object,在Object类中的定义为:

    equals()方法在Object类中的定义:

    public boolean equals(Object obj){
        return (this == obj);
    }
    equals()的定义为:
    public native int hashCode();

    是一个本地方法,返回的对象的地址值。

         1.hashcode()和equals()是在哪里被用到的?什么用的?

         HashMap是基于散列函数以数组和链表的方式实现的

         而对于每一个对象,通过其hashCode()方法可为其生成一个整形值(散列码),而默认自定义类Student 的hashcode是根据对象的引用算的,该整型值被处理后,将会作为数组下标,存放该对象所对应的Entry(存放该对象及其对应值)。

         上边由于两个new Student(1,"aa")是不一样的Student对象。而默认的Student类的hashcode是根据对象的引用算的。所以直接认为是两个不一样的对象,直接put进去了。所以需要重写hashcode方法,如果hashcode不一样则直接认为是不同对象

         equals()方法则是在HashMap中插入值或查询时会使用到。当HashMap中插入 值或查询值对应的散列码与数组中的散列码相等时,则会通过equals方法比较key值是否相等,所以想以自建对象作为HashMap的key,必须重写 该对象继承object的equals方法。

        
          2.本来不就有hashcode()和equals()了么?干嘛要重写,直接用原来的不行么?
         

          HashMap中,如果要比较key是否相等,要同时使用这两个函数!因为自定义的类的hashcode()方法继承于Object类,其hashcode码为默认的内存地 址,这样即便有相同含义的两个对象,比较也是不相等的,例如,

    Student st1 = new Student("wei","man");

    Student st2 = new Student("wei","man"); 

    正常理解这两个对象再存入到hashMap中应该是相等的,但如果你不重写 hashcode()方法的话,比较是其地址,不相等!

          HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等 的。若equals()不相等则认为他们不相等。如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为 同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet判断加入元素 是否相等。
     
    HashSet内部是通过HashMap实现。只有使用排序的时候才使用TreeMap。否知使用HashMap。
    HashSet  set = new HashSet
    set.add(new Student(1,"aa") );
    set.add(new Student(1,"aa") );
    set.add(new Student(2,"aa") );
     
    结果set内的元素为3个,没有去处重复的new Student(1,"aa") ?为什么呢?
    这里由于两个new Student(1,"aa")是不一样的Student对象。而默认的Student类的hashcode是根据对象的引用算的。所以直接认为是两个不一样的对象,直接put进去了。所以需要重写hashcode方法,如果hashcode不一样则直接认为是不同对象,如下:

    package com.svse.map;
    public class Student {
       private int code;
       private String name;

       //重写hashCode方法
       public int hashcode() {
        return code*name.hashCode();
      }

    }

     
    发现还是不对,还是put进去了呢?
    这里重写的hashcode是一样的,所以还是put进去了。所以还需要重新equals方法。其实是有这样一个规定,如果hahscode一样时,则还需要继续调用equals方式看看对象是否相等。如下即可实现:

    输出:

    //重写equals方法
    public boolean equals(Object o) {
      if(o instanceof Student){
       Student stu=(Student) o;
       return name.equalsIgnoreCase(stu.getName().trim());
       }
      return false;
    }


    可以看到如果hashcode不一样就直接认为是不一样的对象,不需要再去equal比较,更加节省时间。
    如果new Student(1,"aa")、new Student(1,"bb")。通过code和name算出的hashcode就可以算是不一样的对象,就不需要再去equals比较。
    往往HashSet中存放的对象是否相等的逻辑都需要自己定义,而并不会直接用默认的引用来算,即一般都会重新hashcode和equals方法,而且同时需要重写。以后要注意哦。

    HashMap的putget也类似。
    HashMap是底层实现时数组加链表。
            A.当put元素时:
                  1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
                  2.如果该下标位置有元素(即根据put元素的key算出的hashcode一样即重复了),则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中。这里由于hashcode一样,所以得出的数组下标位置相同。所以会在该数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。       
            B.当get元素时:
                 根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置一个链表,则需要调用equals方法遍      历链表中的所有元素与当前的元素比较,得到真正想要的对象。
      可以看出如果根据hashcdoe算出的数组位置尽量的均匀分布,则可以避免遍历链表的情况,以提高性能。
       所以要求重写hashcode时,也要重写equals方法。以保证他们是相同的比较逻辑
     
  • 相关阅读:
    数据库特性之原子性和一致性
    [linux] 输出重定向与后台运行
    shell编程其实真的很简单(一)
    Java8中的流操作-基本使用&性能测试
    Hadoop到底是干什么用的?
    为什么要有文件系统?文件系统都有哪些种类?
    MySQL insert value与values
    MySQL create语句
    fiddler抓包-简单易操作(二)
    jmeter文件目录说明(一)
  • 原文地址:https://www.cnblogs.com/zhaosq/p/10063610.html
Copyright © 2011-2022 走看看