Java ==、equals、hashcode的区别

1、== 对于基本变量,判断其值是否相同;对于引用对象,判断其指向的内存地址是否相同。
2、equals 只能应用于引用对象,默认和==的作用相同,如果像String一样继承的话,则可以实现自定义的比较。
3、HashCode仅在Hash容器中使用,用于决定将要插入对象的位置。(一般来说,equals相等的两个对象,它们的hashCode也要相等)

package com.demo.test;

import java.util.HashSet;

public class EqualDemo {

    static class Person implements Cloneable {
        public String idcard;

        public Person(String idcard) {
            this.idcard = idcard;
        }

        //super.clone方法是protected的,这里要将它的可见性手动改为public, 并且删除throws CloneNotSupportedException
        @Override
        public Object clone() {
            return new Person(idcard);
        }
    }

    static class NewPerson {
        public String idcard;

        public NewPerson(String idcard) {
            this.idcard = idcard;
        }

        @Override
        public Object clone() {
            return new NewPerson(idcard);
        }

        @Override
        public boolean equals(Object obj) {
            if(obj instanceof NewPerson) {
                if(idcard != null && idcard.equals(((NewPerson) obj).idcard)) {
                    return true;
                }
            }
            return false;
        }
    }

    static class EvolutionPerson {
        public String idcard;

        public EvolutionPerson(String idcard) {
            this.idcard = idcard;
        }

        @Override
        public Object clone() {
            return new EvolutionPerson(idcard);
        }

        @Override
        public boolean equals(Object obj) {
            if(obj instanceof EvolutionPerson) {
                if(idcard != null && idcard.equals(((EvolutionPerson) obj).idcard)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public int hashCode() {
            return idcard.hashCode();
        }
    }

    public static void main(String[] args) {
        String str1 = "123";
        String str2 = "123";
        String str3 = new String("123");

        System.out.println(str1 == str2);//true
        System.out.println(str1.equals(str2));//true, 这说明对于基本类型变量,equal比较其值,值相同则认为一致
//      System.out.println(str1 == 123);//Error: Incompatible operand types String and int
        System.out.println(str1.equals(123));//false, 在java中,如果类型不一样,肯定不同。如果是javascript则会自动转化
        System.out.println(str1 == str3);//false, 字符串常量在常量池中,而new的字符串存放在堆中,==只比较地址
        System.out.println(str1.equals(str3));//true, string复写了equls方法,先比较==,如果不等,再一个一个字符的进行比较

        Person p1 = new Person("123");
        Person p2 = p1;
        Person p3 = (Person) p1.clone();
        System.out.println(p1.equals(p2));//true, 这说明对于对象equal比较的是引用类型变量的地址,只有其指向的地址一致,equal才会返回true
        System.out.println(p1.equals(p3));//false, 这说明即使引用类型变量的内部变量都一样,地址不一样还是认为是不同的

        NewPerson np1 = new NewPerson("123");
        NewPerson np2 = (NewPerson) np1.clone();
        System.out.println(np1.equals(np2));//true,因为已经覆写了equals方法

        /*
           在HashMap中,查找key的比较顺序为:
         1、计算对象的Hash Code,看在表中是否存在。
         2、检查对应Hash Code位置中的对象和当前对象是否相等。
         3、如果Hash Code相等,但equals不等,则会放到跟该Hash Code有关地址上的一个链式结构中
         */
        HashSet<NewPerson> npSet = new HashSet<NewPerson>();
        npSet.add(np1);
        npSet.add(np2);
        System.out.println(npSet.size());//2, 说明2个对象即使equals返回true,但是还被set判断为不想等

        EvolutionPerson ep1 = new EvolutionPerson("123");
        EvolutionPerson ep2 = (EvolutionPerson) ep1.clone();
        HashSet<EvolutionPerson> epSet = new HashSet<EvolutionPerson>();
        epSet.add(ep1);
        epSet.add(ep2);
        System.out.println(epSet.size());//1, 说明在HashSet中只有hashCode和equals同时相同,才会被判定为一个对象
    }
}

————————— 更新于 2018年5月15日 —————————

通常来说,即使 a 与 b 对象的值相同,但是在内存中的地址不相同,那么两个对象会被认为是不一样的。

Java 的 8 种基本类型 (Byte,Short,Integer,Long,Character,Boolean,Float,Double) 除 Float 和 Double 以外,其它 6 种都实现了常量池。
但是它们只在 [ -128, 127] 区间内时,并且只使用等号(=)赋值而不使用new的情况下,才能使用常量池,超过范围则会被存储到堆内存中。

Integer a = 127, b = 127, c = new Integer(127), d = new Integer(127);
System.out.println(a == b); //true
System.out.println(a == c); //false
System.out.println(c == d); //false
a = -128; b = -128;
System.out.println(a == b); //true
a = 128; b = 128;
System.out.println(a == b); //false
a = -129; b = -129;
System.out.println(a == b); //false

Boolean b1 = true, b2 = true, b3 = new Boolean(true);
System.out.println(b1 == b2);   //true
System.out.println(b1 == b3);   //false

a=1; b = Integer.valueOf(1);
System.out.println(a == b); //true

通过反编译可以看出,使用等号直接赋值,只是调用类的 valueOf 方法的一个语法糖。所以 “常量池” 设在 [ -128, 127] 区间内,在它的源码源码中写明了。

valueOf 的源码如下:

/**
  * Returns an {@code Integer} instance representing the specified
  * {@code int} value.  If a new {@code Integer} instance is not
  * required, this method should generally be used in preference to
  * the constructor {@link #Integer(int)}, as this method is likely
  * to yield significantly better space and time performance by
  * caching frequently requested values.
  *
  * This method will always cache values in the range -128 to 127,
  * inclusive, and may cache other values outside of this range.
  *
  * @param  i an {@code int} value.
  * @return an {@code Integer} instance representing {@code i}.
  * @since  1.5
  */
 public static Integer valueOf(int i) {
     assert IntegerCache.high >= 127;
     if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
 }

IntegerCache 的源码如下:

/**
  * Cache to support the object identity semantics of autoboxing for values between
  * -128 and 127 (inclusive) as required by JLS.
  *
  * The cache is initialized on first usage.  The size of the cache
  * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
  * During VM initialization, java.lang.Integer.IntegerCache.high property
  * may be set and saved in the private system properties in the
  * sun.misc.VM class.
  */

 private static class IntegerCache {
     static final int low = -128;
     static final int high;
     static final Integer cache[];

     static {
         // high value may be configured by property
         int h = 127;
         String integerCacheHighPropValue =
             sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
         if (integerCacheHighPropValue != null) {
             int i = parseInt(integerCacheHighPropValue);
             i = Math.max(i, 127);
             // Maximum array size is Integer.MAX_VALUE
             h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
         }
         high = h;

         cache = new Integer[(high - low) + 1];
         int j = low;
         for(int k = 0; k < cache.length; k++)
             cache[k] = new Integer(j++);
     }

     private IntegerCache() {}
 }

在server模式下,使用-XX:AutoBoxCacheMax=NNN参数即可将Integer的自动缓存区间设置为[-128,NNN]。这个参数是server模式专有的。

Java 中的 equals() 具有如下特性:

对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
反射性:x.equals(x)必须返回是“true”。
类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值