Java基础之自定义类型作为HashMap或HashTable的key需要注意哪些问题

945 阅读4分钟

其实这个问题是答案就是用自定义类作为key,必须重写equals()和hashCode()方法。

以下是Object对象API关于equal方法和hashCode方法的说明:

  • 相等(相同)的对象必须具有相等的哈希码(或者散列码)
  • 两个对象的hashCode相同,它们并不一定相同

举例说明:

相等(相同)的对象必须具有相等的哈希码(或者散列码)

想象一下,假如两个Java对象A和B,A和B相等(eqauls结果为true),但A和B的哈希码不同,则A和B存入HashMap时的哈希码计算得到的HashMap内部数组位置索引可能不同,那么A和B很有可能允许同时存入HashMap,显然相等/相同的元素是不允许同时存入HashMap,HashMap不允许存放重复元素。

两个对象的hashCode相同,它们并不一定相同

也就是说,不同对象的hashCode可能相同;假如两个Java对象A和B,A和B不相等(eqauls结果为false),但A和B的哈希码相等,将A和B都存入HashMap时会发生哈希冲突,也就是A和B存放在HashMap内部数组的位置索引相同这时HashMap会在该位置建立一个链接表,将A和B串起来放在该位置,显然,该情况不违反HashMap的使用原则,是允许的。当然,哈希冲突越少越好,尽量采用好的哈希算法以避免哈希冲突。

Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么Hashset、Hashmap、Hashtable中的存储操作是根据什么原理来存取对象的呢?

下面以HashSet为例进行分析,我们都知道:在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?在java的集合中,判断两个对象是否相等的规则是:

  1. 判断两个对象的hashCode是否相等, 如果不相等,认为两个对象也不相等,完毕。如果相等,转入2(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。

  2. 判断两个对象用equals运算是否相等,如果不相等,认为两个对象也不相等;如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)

为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

class Person {
    String id;
    String name;
    public Person(String id, String name) {
        this.id = id;
        this.name = name;
    }
    public String toString() {
        return "id = " + id + " , name = " + name;
    }
}

class Student {
    String id;
    String name;
    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }
    public int hashCode() {
        return id.hashCode();
    }
    public boolean equals(Object ob) {
        Student student = (Student)ob;
        if(student.id.equals(this.id)) {
            return true;
        }else {
            return false;
        }
    }
    public String toString() {
        return "id = " + id + " , name = " + name;
    }
}

public class HashCodeTest {

    public static void main(String[] args) {
        testStringHash();
        testHashSetWithoutEquals();
        testHashSetWithEquals();
    }
    //测试没有实现equals和hashCode
    public static void testHashSetWithoutEquals() {
        Set<Person> personSet = new HashSet<Person>();
        personSet.add(new Person("123", "Tom"));
        personSet.add(new Person("123", "Tom"));
        Iterator<Person> iterator = personSet.iterator();
        while(iterator.hasNext()) {
            Person person = iterator.next();
            System.out.println(person.toString());
        }
    }
    //测试实现了equals和hashCode
    public static void testHashSetWithEquals() {
        Set<Student> studentSet = new HashSet<Student>();
        studentSet.add(new Student("123", "Tom"));
        studentSet.add(new Student("123", "Tom"));
        Iterator<Student> iterator = studentSet.iterator();
        System.out.println("********************");
        while(iterator.hasNext()) {
            Student student = iterator.next();
            System.out.println(student.toString());
        }
    }
    //String类覆盖了equals和hashCode参考jdk实现
    public static void testStringHash() {
        Object a = new Object();
        String s1 = new String("abc");
        String s2 = new String("abc");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println("hashCode s1 = " + s1.hashCode());
        System.out.println("hashCode s2 = " + s2.hashCode());
        Set hashSet = new HashSet<String>();
        hashSet.add(s1);
        hashSet.add(s2);
        Iterator it = hashSet.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        System.out.println("*******************");
    }
}

同理hashMap的测试如下

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

class Person {
    String id;
    String name;
    public Person(String id, String name) {
        this.id = id;
        this.name = name;
    }
    public String toString() {
        return "id = " + id + " , name = " + name;
    }
}

class Student {
    String id;
    String name;
    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }
    public int hashCode() {
        return id.hashCode();
    }
    public boolean equals(Object ob) {
        Student student = (Student)ob;
        if(student.id.equals(this.id)) {
            return true;
        }else {
            return false;
        }
    }
    public String toString() {
        return "id = " + id + " , name = " + name;
    }
}
public class HashMapTest {
    public static void main(String[] args) {
       testHashMapWithoutEquals();
       testHashMapWithEquals();
    }
    public static void testHashMapWithoutEquals() {
        Map<Person, String> hMap = new HashMap<Person, String>();
        Person person1 = new Person("123", "Tom");
        Person person2 = new Person("123", "Tom");
        hMap.put(person1, "address");
        hMap.put(person2, "address");
        Iterator iterator = hMap.entrySet().iterator();
        while(iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            Person key = (Person) entry.getKey();
            String val = (String) entry.getValue();
            System.out.println("key = " + key + "  value = " + val);
        }
    }
    public static void testHashMapWithEquals() {
        System.out.println("*********************");
        Map<Student, String> hMap = new HashMap<Student, String>();
        Student student1 = new Student("123", "Tom");
        Student student2 = new Student("123", "Tom");
        hMap.put(student1, "address");
        hMap.put(student2, "address");
        Iterator iterator = hMap.entrySet().iterator();
        while(iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            Student key = (Student) entry.getKey();
            String val = (String) entry.getValue();
            System.out.println("key = " + key + "  value = " + val);
        }
    }
}

相关文章

  1. www.cnblogs.com/Qian123/p/5…