zoukankan      html  css  js  c++  java
  • java重写equals和hashCode方法


    一.重写equals方法
     如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。
     利用equals比较八大包装对象(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址.
     
     重写规则
     (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方法修饰符必须是public,因为是重写的Object的方法; 参数类型必须是Object.
     
     重写equals方法后必须重写hashCode方法,否则两个等价对象可能得到不同的hashCode,这在集合框架中使用可能产生严重后果
     (1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
     (2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false

    二.重写hashCode方法
     hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashCode值来进行判断是否相同的。

     通用的hashCode算法实现
     1.首先初始化一个整形变量,为此变量赋予一个非零的常数值,比如
      int result = 17;
      初始值要使用非0的整数,以减少hash冲突.假如所有的域引用都为null,按照下面的算法,采用0作为初始值则计算出来的结果都为0.
     2.选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:
      (1) 如果是boolean类型,则计算根据是否为真进行处理,如: f ? 1231 : 1237
      (2) 如果是bytecharshortint类型,则计算(int)f
      (3) 如果是long类型,则计算(int)(f ^ (f >>> 32))
      (4) 如果是float类型,则计算Float.floatToIntBits(f)
      (5) 如果是double类型,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int
      (6) 如果是对象引用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。
        否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode值为0
      (7) 如果是数组,那么需要为每个元素当做单独的域来处理.
      如果你使用的是1.5及以上版本的JDK,那么没必要自己去重新遍历一遍数组,java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上.
      ps:上面所提到的基本元素类型的hashCode计算,其算法同JDK中其包装器类型所覆盖的hashCode逻辑一致,具体如下
       java.lang.Long的hashCode实现:
        public int hashCode() {
         return (int)(value ^ (value >>> 32));
        } 
       java.lang.Float的hashCode实现:
         public int hashCode() {
          return floatToIntBits(value);
         }
       java.lang.double的hashCode实现:
         public int hashCode() {
          long bits = doubleToLongBits(value);
          return (int)(bits ^ (bits >>> 32));
         }
       java.lang.boolean的hashCode实现
        public int hashCode() {
         return value ? 1231 : 1237;
        }
       其它类型,可按如下实现
       java.lang.byte的hashCode实现,charshortint同理也可按此实现
        public int hashCode() {
         return value;
        }
     3.将上述hashCode值与质数因子进算得到新的值
      result = result * 31 + hashCode();
     4.此算式的几点解释:
      使用乘法去操作result: 目的是为了使散列值依赖于域的顺序.如同时包含了两个相同类型的域,以保证顺序不同产生不同的hashCode.
      使用31作为质数因子: 31是个神奇的数字,因为任何数n * 31就可以被JVM优化为(n << 5) - n, 移位和减法的操作效率要比乘法的操作效率高的多.
      
    三.一个具体的实现类的例子

    class Item {
        private String mName;
        private long mId;
        private long mValue;
    
        public Item(String name, long id) {
            mName = name;
            mId = id;
        }
    
        public Item(String name, long id, long value) {
            mName = name;
            mId = id;
            mValue = value;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (!(obj instanceof Item))
                return false;
            Item object = (Item) obj;
    
            if (mName == null) {
                if (object.mName != null)
                    return false;
            } else if (!mName.equals(object.mName)) {
                return false;
            }
    
            if (mId != object.mId)
                return false;
    
            if (mValue != object.mValue)
                return false;
    
            return true;
        }
    
        @Override
        public int hashCode() {
            int result = 17;
            result = 31 * result + (mName != null ? mName.hashCode() : 0);
            result = 31 * result + (int) (mId ^ (mId >>> 32));
            result = 31 * result + (int) (mValue ^ (mValue >>> 32));
            return result;
            // HashCodeBuilder builder = new HashCodeBuilder();
            // builder.append(mId).append(mName).append(mValue);
            // return builder.getHashCode();
        }
    
        @Override
        public String toString() {
            return "Item[mName=" + mName + ", mId=" + mId + ", mValue=" + mValue
                    + "]";
        }
    }

     
      
    四.一个hashCode生成器类

    /**
     * hashCode生成器
     */
    public class HashCodeBuilder {
    
        private final int mMultiplierFactor;
        private int mHashCode;
    
        public HashCodeBuilder() {
            this(17, 31);
        }
    
        public HashCodeBuilder(int initialCode) {
            this(initialCode, 31);
        }
    
        public HashCodeBuilder(int initialCode, int multiplierFactor) {
            mHashCode = initialCode;
            mMultiplierFactor = multiplierFactor;
        }
    
        public int getHashCode() {
            return mHashCode;
        }
    
        public HashCodeBuilder append(int[] array) {
            for (int element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(byte[] array) {
            for (byte element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(short[] array) {
            for (short element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(char[] array) {
            for (char element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(long[] array) {
            for (long element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(float[] array) {
            for (float element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(double[] array) {
            for (double element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(boolean[] array) {
            for (boolean element : array) {
                append(element);
            }
            return this;
        }
    
        public HashCodeBuilder append(int element) {
            mHashCode = mHashCode * mMultiplierFactor + element;
            return this;
        }
    
        public HashCodeBuilder append(byte element) {
            mHashCode = mHashCode * mMultiplierFactor + element;
            return this;
        }
    
        public HashCodeBuilder append(short element) {
            mHashCode = mHashCode * mMultiplierFactor + element;
            return this;
        }
    
        public HashCodeBuilder append(char element) {
            mHashCode = mHashCode * mMultiplierFactor + element;
            return this;
        }
    
        public HashCodeBuilder append(long element) {
            mHashCode = mHashCode * mMultiplierFactor
                    + ((int) (element ^ (element >>> 32)));
            return this;
        }
    
        public HashCodeBuilder append(float element) {
            return append(Float.floatToIntBits(element));
        }
    
        public HashCodeBuilder append(double element) {
            return append(Double.doubleToLongBits(element));
        }
    
        public HashCodeBuilder append(boolean element) {
            mHashCode = mHashCode * mMultiplierFactor + (element ? 1231 : 1237);
            return this;
        }
    
        /**
         * 处理自定义类型元素或数组
         * @param object,数组或一个元素
         * @return
         */
        public HashCodeBuilder append(Object object) {
            if (object == null)
                return appendNull();
            
            Class<?> klassType = object.getClass().getComponentType();
            // klass为null,表示一个元素,否则表示一个数组
            if (klassType == null) {
                return appendElement(object);
            }
            return deepHashCode((Object[])object);
        }
    
        private HashCodeBuilder deepHashCode(Object[] array) {
            if (array == null) {
                return appendNull();
            }
            for (Object element : array) {
                deepHashCodeElement(element);
            }
            return this;
        }
    
        private HashCodeBuilder deepHashCodeElement(Object element) {
            if (element == null) {
                return appendNull();
            }
    
            Class<?> klass = element.getClass().getComponentType(); 
    
            // klass为null,表示一个元素,否则表示一个数组
            if (klass == null) {
                return appendElement(element);
            }
            
            // isPrimitive返回true则为基本类型,如 intchar...等
            if (!klass.isPrimitive()) {
                return deepHashCode((Object[]) element);
            }
    
            if (klass.equals(int.class)) {
                return append((int[]) element);
            }
            if (klass.equals(char.class)) {
                return append((char[]) element);
            }
            if (klass.equals(boolean.class)) {
                return append((boolean[]) element);
            }
            if (klass.equals(byte.class)) {
                return append((byte[]) element);
            }
            if (klass.equals(long.class)) {
                return append((long[]) element);
            }
            if (klass.equals(float.class)) {
                return append((float[]) element);
            }
            if (klass.equals(double.class)) {
                return append((double[]) element);
            }
            return append((short[]) element);
        }
    
        private HashCodeBuilder appendElement(Object element) {
            if (element == null)
                return appendNull();
    
            Class<?> klass = element.getClass();
    
            // klass 对于基本类型将自动包装
            if (klass.equals(Integer.class)) {
                int value = (Integer) element;
                return append(value);
            }
    
            if (klass.equals(Character.class)) {
                char value = (Character) element;
                return append(value);
            }
    
            if (klass.equals(Boolean.class)) {
                boolean value = (Boolean) element;
                return append(value);
            }
    
            if (klass.equals(Byte.class)) {
                byte value = (Byte) element;
                return append(value);
            }
    
            if (klass.equals(Short.class)) {
                short value = (Short) element;
                return append(value);
            }
    
            if (klass.equals(Long.class)) {
                long value = (Long) element;
                return append(value);
            }
    
            if (klass.equals(Float.class)) {
                float value = (Float) element;
                return append(value);
            }
    
            if (klass.equals(Double.class)) {
                double value = (Double) element;
                return append(value);
            }
    
            // 非基本类型元素,调用其自身的hashCode方法进行计算
            mHashCode = mHashCode * mMultiplierFactor + element.hashCode();
            return this;
        }
    
        /**
         * 处理null值对象
         * @return
         */
        private HashCodeBuilder appendNull() {
            mHashCode = mHashCode * mMultiplierFactor + 0;
            return this;
        }
    }


     

  • 相关阅读:
    autoMapper dotnetcore webapi 自动添加映射 abp
    win10安装MongoDB提示 the domain,user name and/or password are incorrect. Remember to use "." for the domain if the account is on the local machine.
    webapi 重复提交问题
    webapi postman 415 错误
    sqlserver 更新通过 select 查询出的结果集
    2016-03至2016-08前端工作总结
    css笔记——css 实现自定义按钮
    javascript笔记——date以及datetime的比较
    node.js笔记——gulp
    javascript笔记——密码组合规则
  • 原文地址:https://www.cnblogs.com/diysoul/p/5123938.html
Copyright © 2011-2022 走看看