从HashMap 的 key 看 Java 中"=="与"equals"的区别

在Java中如何判断两个变量是否相等是很重要的。一般情况下,地址相等那么内容肯定相等,反之不是。因而,在判断相等与否时,我们需要分清需求,到底是地址相等还是内容相等。

所以引申出两种判断方法(需重写equals为该逻辑后实现):

“==” 比较的是值:变量(栈内存)中存放的对象的堆内存地址

“equals”比较两个对象的内容(栈内存中存放的内容,不是比地址)

对基本/引用类型的区别:

  • 基本类型:==和Equals都比较两个值是否相等。相等为true 否则为false;
  • 引用类型:==和Equals搜索都是比较栈内存中的地址是否相等 。相等为true 否则为false;

String 类型的特殊性

对于字符串变量来说,使用“==”和“equals()”方法比较字符串时,其比较方法不同。

  • “==”比较两个变量本身的值,即两个对象在内存中的首地址。 “equals()”比较字符串中所包含的内容是否相同
String s1,s2,s3 = "abc", s4 ="abc" ;
s1 = new String("abc");
s2 = new String("abc");
s1==s2   //false:两个变量的内存地址不一样,也就是说它们指向的对象不一样,
s1.equals(s2) //true:两个变量的所包含的内容是abc,故相等。
s3==s4 //true:s3和s4是两个字符串常量所生成的变量,"所存放的内存地址"是相等的,即使没有s3=s4赋值语句

HashMap 中相同key的判断

结论:
- 除非String类型,否则重写equals(根据内容相同找相同的key)和hashCode(根据内容相同找相同的桶)。
- 如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
- 如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false) 。
源码如下:

//通过key的hashCode计算hash值,然后通过idexFor找到第i个桶
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length); 
//通过遍历相同key链表上的元素 e=e.next,根据==||equals判断是否为key相同
if (e.hash == hash && ((k = e.key) == key || key.equals(k))
//示例代码:
public int hashCode() {  
        int result = 17;  
        result = result * 31 + name.hashCode();  
        result = result * 31 + age;  

        return result;  
    }  
public boolean equals(Object other) { 
 if(other == this)  
            return true;  
if(!(other instanceof Coder))  
            return false;  
Coder o = (Coder)other;  
        return o.name.equals(name) && o.age == age; 
      } 

注意
- Object类中的equals方法和“==”是一样的,没有区别,而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”;
- “==”比”equal”运行速度快,因为”==”只是比较引用.

 // 判断两个词是否是同构词,需要注意 "","",用 == 判断不相同,用equals判断就对了.
    public boolean isAnagram(String s, String t) {
        char[] s1 = s.toCharArray();
        char[] s2 = t.toCharArray();
        Arrays.sort(s1);
        Arrays.sort(s2);
        //这里 s1 和 s2并不是字符串常量生成,所以地址不同,==不相等。
        if(new String(s1).equals(new String(s2)))
            return true;
        else return false;
    }
    public static void main(String[] args){
        String s1="";
        String s2="";
        GroupAnagrams g = new GroupAnagrams();
        System.out.print(g.isAnagram(s1,s2));
    }

附录 1: HashMap源码之put实现

// hash和key均相同,则用新的Entry的value覆盖原来节点的value。如果两个hash值相等但key值不等 ,则将该节点插入该链表的链头。
public V put(K key, V value) {
        //当key为null,调用putForNullKey方法,保存null与table第一个位置中,这是HashMap允许为null的原因
        if (key == null)
            return putForNullKey(value);
        //计算key的hash值
        int hash = hash(key.hashCode());                  ------(1)
        //计算key hash 值在 table 数组中的位置
        int i = indexFor(hash, table.length);             ------(2)
        //从i出开始迭代 e,找到 key 保存的位置
        for (Entry e = table[i]; e != null; e = e.next) {
            Object k;
            //判断该条链上是否有hash值相同的(key相同)
            //若存在相同,则直接覆盖value,返回旧value
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;    //旧值 = 新值
                e.value = value;
                e.recordAccess(this);
                return oldValue;     //返回旧值
            }
        }
        //修改次数增加1
        modCount++;
        //将key、value添加至i位置处
        addEntry(hash, key, value, i);
        return null;
    }

参考资料

HashMap原理

你可能感兴趣的:(java)