`
DavyJones2010
  • 浏览: 151777 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java SE: hashCode() & equals() and HashMap

阅读更多

1. Both hashCode() and equals() are defined in Object:

public native int hashCode();
public boolean equals(Object obj) {
    return (this == obj);
}

    If our customized object doesn't override hashCode(), then hashCode will be generated according to the object's address.

    If our customized object doesn't override equals(), then equals() will compare the addresses of the two objects.

    Thus we can conclude that if two objects are equals, then their hashCode must be the same, because their address are the same.

    If we use the default hashCode() method in Object, then we can also guarantee that different object will produce different hashcode.

    "This function(hashCode) produces hashcode by typically converting the internal address of the object into and integer, thus produce different hashcodes for all different objects."

    Remember that hashCode is useful only when the Object is intented to be put in a hash based container, such as HashMap or HashSet.

 

2. String.hashCode() & String.equals()

    String has override the default hashCode() and equals() method.

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                        return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
         for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

    We can find in method above that the equals() will compare the literal value of the String,

    and the hashCode() will calculate hashCode based on the literal value of the String.

    Thus we can conclude that if two String are equals, then their hashCode must be the same

    as they have the same literal value and hash code is calculated based on literal value,

 

3. HashMap & hashCode() & equals()

    The structure of HashMap is:

public class HashMap<K,V>
{
    /**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;

    static final int MAXIMUM_CAPACITY = 1 << 30;
    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry<K,V>[] table;
    /**
     * The number of key-value mappings contained in this map.
     */
    transient int size;
    /**
     * The next size value at which to resize (capacity * load factor).
     * @serial
     */
    int threshold;
    /**
     * The load factor for the hash table.
     *
     * @serial
     */
    final float loadFactor;

    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        //....
    }
}

    HashMap is backed by a bucket: Entry<K, V>[] table.

    When we put key&value into the table.

        1> key is checked for null. If key is null, the value is stored in table[0] position, because hashcode for null is always 0.

        2> Calculate the hashcode of key using hash(),

             the reason JDK introduced hash() instead of simply using key.hashCode() is that

             there might be some poorly written hashCode() function that can return very high or low hash code value

             and thus make the hashCode out of table size range.

        3> Find the index in table according to hashCodeOfKey.

        4> Find if we have the key in the table/bucket already, if true, then we will put the new key/value in the position then return.

        5> Add the new entry into the head of LinkedList in table[index] position.

    Eg1.

package edu.xmu.java.core;

import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

public class HashMapTest {

    @Test
    public void putTest() {
	Map<Student, String> map = new HashMap<Student, String>();
	Student s = new Student("AAA", 12);
	Student s2 = new Student("BBB", 14);
        Student s3 = new Student("AAA", 12);
	map.put(s, "DUMMY_VAL");
	map.put(s2, "DUMMY_VAL");
        map.put(s3, "DUMMY_VAL");
	assertEquals(3, map.size());
    }

    public static class Student {
	private String name;
	private int age;

	public Student(String name, int age) {
	    super();
	    this.name = name;
	    this.age = age;
	}

	@Override
	public int hashCode() {
	    return 1;
	}
    }
}

    The three objects: s, s2, s3 sits in the same position of the bucket because they have the same hashCode, and s3.next=s2; s2.next=s1.

    If we assume that s, s3 is the same object, then we should override equals() for Student, because it is what we use to judge whether they are the same objects.

    The hashCode() method is only for index of the bucket, it is just for the purpose of quick search.

    Eg2:

package edu.xmu.java.core;

import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

public class HashMapTest {

    @Test
    public void putTest() {
	Map<Student, String> map = new HashMap<Student, String>();
	Student s = new Student("AAA", 12);
	Student s2 = new Student("BBB", 14);
	Student s3 = new Student("AAA", 12);
	map.put(s, "DUMMY_VAL");
	map.put(s2, "DUMMY_VAL");
	map.put(s3, "DUMMY_VAL");
	assertEquals(3, map.size());
    }

    public static class Student {
	private String name;
	private int age;

	public Student(String name, int age) {
	    super();
	    this.name = name;
	    this.age = age;
	}

	@Override
	public boolean equals(Object obj) {
	    if (this == obj)
		return true;
	    if (obj == null)
		return false;
	    if (getClass() != obj.getClass())
		return false;
	    Student other = (Student) obj;
	    if (age != other.age)
		return false;
	    if (name == null) {
		if (other.name != null)
		    return false;
	    } else if (!name.equals(other.name))
		return false;
	    return true;
	}

    }
}

    Assumption1: If we override the equals but didn't override hashcode, thus hashcode is calculated based on the object's address.

    And thus in the HashMap, s and s3 have different index in the bucket, they never have the chance to compare with each other.

    // s.hash != s3.hash

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    V oldValue = e.value;
    e.value = value;
    e.recordAccess(this);
    return oldValue;
}

    It would make no sence, thus the best practice is that if we override equals() method, that means our customized object will not compare based on address and might compare based on some of its properties, then we should override hashCode accordingly to avoid that we have duplilcate objects in hash based container, like HashMap or HashSet.

    Assumption2: If we override hashCode, say return the same hashCode for every instances, and didn't override equals, all instances would be stored in the hash based container, and they will be stored in the same index of the bucket-table, it would be degenerated into an LinkedList. And thus the performance of hash container would be largely affected.

    The best practice is that if we want to override any of hashCode() and equals(), then we should override the other one at the same time.

 

 

Reference Links:

1) http://stackoverflow.com/questions/17803775/why-can-index-of-backing-table-in-java-util-hashmap-be-the-same-for-two-differen

2) http://howtodoinjava.com/2012/10/09/how-hashmap-works-in-java/

分享到:
评论

相关推荐

    Java理论与实践:hashCode()和equals()方法

    本文介绍了Java语言不直接支持关联数组,可以使用任何对象作为一个索引的数组,但在根Object类中使用 hashCode()方法明确表示期望广泛使用HashMap。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式...

    HashCode相同equals不同的2位字符集合算法

    在Java编程语言中,`hashCode()` 和 `equals()` 是两个非常重要的方法,它们主要用于对象的比较和哈希表(如HashMap)的操作。标题提到的"HashCode相同equals不同的2位字符集合算法"涉及到的是一个特定场景:两个...

    Java_重写equals()和hashCode()

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是对象的基本组成部分,它们在很多场景下都发挥着至关重要的作用。这两个方法与对象的相等性比较和哈希表(如HashMap、HashSet)的运作紧密相关。这篇博客将深入...

    java中hashcode()和equals()方法详解

    ### Java中`hashCode()`与`equals()`方法详解 #### 前言 在Java编程语言中,`hashCode()`和`equals()`方法是非常重要的概念,它们不仅对于深入理解Java内存管理至关重要,也是实现自定义类的关键部分之一。本文将...

    java中hashcode()和equals()的详解

    在Java编程语言中,`hashCode()`和`equals()`方法是对象身份验证的关键组成部分,它们主要用于对象的比较和哈希表(如HashMap、HashSet等)的操作。理解这两个方法的工作原理对于编写高效和可靠的代码至关重要。 ...

    解析Java对象的equals()和hashCode()的使用

    深入解析Java对象的equals()和hashCode()的使用 在Java语言中,equals()和hashCode()两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个。在多数情况下,这两个函数是不用考虑的,直接使用它们...

    重写equals和hashcode方法_equals_重写equals和hashcode方法_

    在使用Java集合类时,如HashSet或HashMap,`equals()` 和 `hashCode()` 的一致性非常重要。如果你只重写了 `equals()` 而不重写 `hashCode()`,可能会导致无法正确地将对象添加到集合或从集合中查找对象。例如,在...

    java中hashCode、equals的使用方法教程

    在Java编程语言中,`hashCode()` 和 `equals()` 方法对于对象的比较和处理至关重要,尤其在集合类(如Set和Map)中。这两个方法都源自`java.lang.Object`类,因此所有的Java类都默认继承了它们。理解并正确地重写这...

    java HashMap原理分析

    为了解决这些问题,HashMap需要重写hashCode和equals方法,以确保两个不同的Key具有不同的哈希码和equals结果。同时,HashMap也需要处理哈希碰撞问题,例如使用链表来存储发生哈希碰撞的Key-Value对。 HashMap是一...

    重写hashCode()和equals()方法详细介绍

    在Java编程中,`equals()` 和 `hashCode()` 方法是Object类中的两个重要方法,它们在处理对象相等性以及在哈希表(如HashSet、HashMap)中起到关键作用。当自定义类时,有时需要根据业务逻辑重写这两个方法以满足...

    java中hashcode()和equals()的详解.docx

    在Java编程中,`hashCode()`和`equals()`方法是两个非常重要的概念,它们对于理解和使用Java中的集合框架(尤其是`HashSet`和`HashMap`)至关重要。本文将详细介绍这两个方法的基本原理及其在Java中的实现方式。 ##...

    Java重写equals及hashcode方法流程解析

    "Java重写equals及hashcode方法流程解析" Java中的equals和hashCode方法是两个非常重要的方法,它们都是Object类中的方法。在实际开发中,正确地重写这两个方法对于确保程序的正确性和性能至关重要。下面,我们将...

    java中Hashcode的作用.docx

    在Java中的散列表(如HashMap、HashSet等)中,Hashcode扮演着关键角色。它可以快速地判断两个对象是否相等,从而提高散列表的性能。 证明Hashcode不是内存地址 有人认为Hashcode是对象在内存中的地址,但这是一种...

    java 基础之(equals hashcode)

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是两个非常重要的概念,尤其是在处理对象比较和哈希表(如 `HashMap` 和 `HashSet`)时。`equals()` 方法用于判断两个对象是否相等,而 `hashCode()` 方法则用于...

    leetcode题库-java-interview:Java研发基础相关

    Java-Interview 四大基本特性 重载与重写的区别 访问控制符 Object类方法 抽象类与接口 类初始化顺序 hashCode & equals == & equals this static 基本类型 & 包装类 String 泛型 内部类 集合类 ArrayList & ...

    hashcode()和equals()

    在Java编程语言中,`hashCode()` 和 `equals()` 方法是两个非常重要的概念,尤其是在处理对象比较和哈希表(如 `HashMap` 或 `HashSet`)时。这两个方法来源于 `Object` 类,是所有Java类的基类,因此,每个自定义类...

    javahashcode()和equals()和==的介绍和区别.pdf

    在Java编程中,`hashCode()`、`equals()`以及`==`是三个经常被提及的概念,它们在处理对象的比较和存储时起着关键作用。本文将深入探讨这三个概念的介绍、区别以及它们在Java对象比较中的应用。 首先,`hashCode()`...

    深入理解Java中HashCode方法

    深入理解Java中HashCode方法 Java中的hashCode方法是每个类都需要实现的重要方法之一,它的主要作用是将对象的数据转换为一个32位的整数,用于标识对象的唯一性。在Java的所有类中,Object类是最顶层的父类,它定义...

    Java容器集合(equals 和 hashCode+基础数据结构+ArrayList+Vector和LinkedList)

    Java容器集合(equals和hashCode+基础数据结构+ArrayList+Vector和LinkedList) Java容器集合是Java中的一种基础数据结构,用于存储和管理数据。其中,equals和hashCode方法是Java容器集合中两个非常重要的方法,...

Global site tag (gtag.js) - Google Analytics