zoukankan      html  css  js  c++  java
  • hashCode()方法和equals方法的重要性。

    在Object中有两个重要的方法:hashCode()和equals(Object obj)方法,并且当你按ctrl+alt+s时会有Generator hashCode()和equals()。我们不禁会想这两个方法到底有什么用,让eclipse提供自动生成这两个方法的模板呢?

    这两个方法主要是在hash的数据结构中。如HashSet<E> 、 HashMap<K,V>中。

    内容提要:

    下面的代码ElementWithoutHashAndEqual类中定义了字段、构造方法、把hashCode()和equals()方法注释掉。然后再HashTest这个类中进行测试,看看运行结果。然后你再把ElementWithoutHashAndEqual类中的hashCode()和equals()方法的注释去掉,你

    再看看运行结果是什么。这时你就会发现hashCode()和equals()方法的重要性了。 接着再从源码的角度看看为什么需要些这两个方法.

    //ElementWithoutHashAndEqual类的代码如下:

     1 package com.qls.hashAndEquals;
     2 
     3 public class ElementWithoutHashAndEqual {
     4     private int age;
     5     private String name;
     6     
     7     public ElementWithoutHashAndEqual(int age, String name) {
     8         this.age = age;
     9         this.name = name;
    10     }
    11     
    12     /*@Override
    13     public int hashCode() {
    14         final int prime = 31;
    15         int result = 1;
    16         result = prime * result + age;
    17         result = prime * result + ((name == null) ? 0 : name.hashCode());
    18         return result;
    19     }
    20 
    21     @Override
    22     public boolean equals(Object obj) {
    23         if (this == obj)
    24             return true;
    25         if (obj == null)
    26             return false;
    27         if (getClass() != obj.getClass())
    28             return false;
    29         ElementWithoutHashAndEqual other = (ElementWithoutHashAndEqual) obj;
    30         if (age != other.age)
    31             return false;
    32         if (name == null) {
    33             if (other.name != null)
    34                 return false;
    35         } else if (!name.equals(other.name))
    36             return false;
    37         return true;
    38     }*/
    39 
    40     public String toString() {
    41         return "ElementWithoutHashAndEqual [age=" + age + ", name=" + name + "]";
    42     }
    43     public static void main(String[] args) {
    44         // TODO Auto-generated method stub
    45 
    46     }
    47 
    48 }

    //HashTest的代码如下:

     1 package com.qls.hashAndEquals;
     2 
     3 import java.lang.reflect.InvocationTargetException;
     4 import java.util.HashMap;
     5 import java.util.HashSet;
     6 
     7 /**在HashSet<E>,HashMap<K,V>中的元素E和K,必须要重写hashCode()和equals方法。
     8  * 在大多数情况下E 写成String,Integer
     9  * 等java自带的类。其实这些类已经重写了Object类中hashCode()和equals方法。
    10  * 所以你感觉不到hashCode()方法和equals()方法的重要性。
    11  * 这里我们自定义一个类,看它不重写hashCode()和equals方法会有什么后果。
    12  * @author 秦林森
    13  *
    14  */
    15 public class HashTest {
    16     
    17     public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
    18         // TODO Auto-generated method stub
    19         /**
    20          * 用HashSet做例子看HashSet<E>中的元素E类中不重写hashCode()方法,和equals(Object obj);方法
    21          * 的效果:
    22          * 下面HashSet的遍历结果为:
    23          * ElementWithoutHashAndEqual [age=18, name=sixi]
    24             ElementWithoutHashAndEqual [age=18, name=sixi]
    25             ElementWithoutHashAndEqual [age=18, name=sixi]
    26             咋看以下是不是感觉这违背了HashSet不能加重复元素的基本原则。
    27             其实这并没有违背因为Object类中的equals方法是比较两个对象的地址的。
    28             那么hashCode()为什么要重写呢?
    29             再看一看HashMap做例子的情况吧!
    30          */
    31         HashSet<ElementWithoutHashAndEqual> hashSet = new HashSet<ElementWithoutHashAndEqual>();
    32         hashSet.add(new ElementWithoutHashAndEqual(18, "sixi"));
    33         hashSet.add(new ElementWithoutHashAndEqual(18, "sixi"));
    34         hashSet.add(new ElementWithoutHashAndEqual(18, "sixi"));
    35         for(ElementWithoutHashAndEqual e2:hashSet){
    36             System.out.println(e2);
    37         }
    38         /**
    39          * HashMap做例子看看HashMap<K,V>中的元素K这个类不重写hashCode()和equals方法的严重性。
    40          */
    41         HashMap<ElementWithoutHashAndEqual, Integer> hashMap=new HashMap<>();
    42         //先用反射向HashMap中添加元素:
    43         hashMap.put(ElementWithoutHashAndEqual.class
    44                 .getConstructor(new Class[]{int.class,String.class})
    45                 .newInstance(new Object[]{29,"ouyangfeng"})
    46                 , 1);
    47         //我们再看看用get(K key)方法能不能把value取出来。
    48         Integer value = hashMap.get(ElementWithoutHashAndEqual.class
    49                 .getConstructor(new Class[]{int.class,String.class})
    50                 .newInstance(new Object[]{29,"ouyangfeng"}));
    51         System.out.println("value="+value);//输出结果为value=null,而不是value=1.
    52         /**
    53          * 直接new一个对象加在HashMap中。这时你就会看得非常明白了,我之所以用反射创建对象,
    54          * 因为这种创建对象方式比较不容易看懂,从而迷惑你以为put(key,value) 和get(key)中的key是
    55          * 同一个对象。
    56          */
    57         hashMap.put(new ElementWithoutHashAndEqual(6, "AbrahamLincoln"), 3);
    58         //下面语句的输出结果还为value=null.
    59         System.out.println("value="+hashMap.get(new ElementWithoutHashAndEqual(6, "AbrahamLincoln")));
    60         /**
    61          * 但是当你把ElementWithoutHashAndEqual这个类中的hashCode()、equals()方法的注释取消时,
    62          * 输出结果就会变为你所想要的结果了,结果如下:
    63          * ElementWithoutHashAndEqual [age=18, name=sixi]
    64             value=1
    65             value=3
    66             生成hasCode和equals方法如下:
    67             ctr+alt+s---->Generate hashCode() and equals()。便可以生成这两个函数了。
    68             下面将出源码中看为什么需要在HashSet<E> 和HashMap<K,V>中的E、 K需要这两个函数。
    69          */
    70     }
    71 
    72 }

    分析为什么需要hashCode()和equals()方法。分析如下:

    首先讲在HashMap中如何根据key,得到value的。

    第一步:int hash=hash(key)【这个函数调用了key类中的hashCode()函数】得到某个值,

    第二步:根据函数int i=indexFor(hash,table.length);找到这个i值在数组table的值table[i],这个table[i]中的值就是Map.Entry<K,V>【其中Entry<K,V> 为Map中的一个静态类】

    第三步:有可能多个对象产生的hashCode()值一样,所以在源码中进行了这样的遍历:

    for (Entry<K,V> e = table[i]; e != null; e = e.next) {//这个遍历意思就是table[i]中所存放的值Entry<K,V>可能不止一个。用到了链表的数据结构,对各个节点进行遍历。
    Object k;
    if (e.hash == hash && ((k = e.key) == key || key.equals(k)))//对Entry<K,V>进行筛选,找到符合的Entry<K,V>,这个必然只有一个。

    第四步:用Entry<K,V>中的getValue(),便可以取得value值了。上述步骤所用到的源码如下所示:

     1  final Entry<K,V> getEntry(Object key) {
     2         if (size == 0) {
     3             return null;
     4         }
     5 
     6         int hash = (key == null) ? 0 : hash(key);
     7         for (Entry<K,V> e = table[indexFor(hash, table.length)];
     8              e != null;
     9              e = e.next) {
    10             Object k;
    11             if (e.hash == hash &&
    12                 ((k = e.key) == key || (key != null && key.equals(k))))
    13                 return e;
    14         }
    15         return null;
    16     }
    1 public V get(Object key) {
    2         if (key == null)
    3             return getForNullKey();
    4         Entry<K,V> entry = getEntry(key);
    5 
    6         return null == entry ? null : entry.getValue();
    7     }

    在HashSet 中其实它添加元素也用到了Map中的方法,源码如下所示:

    public boolean add(E e) {
    return map.put(e, PRESENT)==null;
    }。

  • 相关阅读:
    go语言基础之map赋值、遍历、删除 、做函数参数
    go语言基础之map介绍和使用
    go语言基础之go猜数字游戏
    go语言基础之切片做函数参数
    ORA-28001: the password has expired解决方法
    windows10下设置Maven的本地仓库和阿里云的远程中央仓库
    mvn安装
    elk基于jolokia监控springboot应用jvm方案
    陌陌风控系统
    ElasticSearch SIEM方案
  • 原文地址:https://www.cnblogs.com/1540340840qls/p/6221208.html
Copyright © 2011-2022 走看看