1. 程式人生 > >自定義類作為Map key的條件

自定義類作為Map key的條件

一,大家通常會用HashMap存放資料,key一般用的是String,Integer這種基本資料型別,但是用自定義物件或者泛型作為key的情況會出現put進去get不到的情形,如下程式碼:

public class MapKey {

    public static void main(String[] args) {
        class User {
            private long id;
            private String name;
            private int age;

            public long getId() {
                return id;
            }

            public void setId(long id) {
                this.id = id;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public int getAge() {
                return age;
            }

            public void setAge(int age) {
                this.age = age;
            }
            public User(long id, String name, int age){
                this.id = id;
                this.name = name;
                this.age = age;
            }

        }
        Map<User, String> map = new HashMap<>();
        User user1 = new User(1l,"hu", 28);
        User user2 = new User(1l,"hu", 28);
        map.put(user1, "hujt");
        System.out.println(user1.equals(user2));
        System.out.println(map.get(user2));

    }
false
null
輸出如上,false和null,就是說user1與user2不等,put進去的new User(1l, "hujt", 28),用new User(1l, "hujt", 28)取到的是空。為什麼呢?

二,看下HashMap get資料的原始碼如下:

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
獲取key對應值就是對key進行hashcode,遍歷hashmap找到key的hashcode與前面的hashcode的值相等,且key的值本身也是相等。那為什麼兩個物件user1和user2的hashcode不相等呢?每個物件都繼承Objcet的hashCode方法,
/** As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java™ programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();
由上可知hashCode()是虛擬機器本地方法,註釋中有一句話 (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java&trade; programming language.),意思是hashcode返回的值是根據實體地址轉成的整數,但是這不是java語言本身去實現的。那麼新生成的兩個物件的實體地址肯定不一樣,導致hashcode值不一致,所以取到的是空。所以這裡需要重寫hashCode()和equals()方法。程式碼如下
public class MapKey {

    public static void main(String[] args) {
        class User {
            private long id;
            private String name;
            private int age;

            public long getId() {
                return id;
            }

            public void setId(long id) {
                this.id = id;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public int getAge() {
                return age;
            }

            public void setAge(int age) {
                this.age = age;
            }
            public User(long id, String name, int age){
                this.id = id;
                this.name = name;
                this.age = age;
            }
            @Override
            public String toString() {
                return this.id + "," + this.name + "," + this.age;
            }
            @Override
            public boolean equals(Object object) {
                if(this == object) return true;
                if(object instanceof User) {
                    User user = (User)object;
                    return this.id == user.id && this.name.equals(user.name) && this.age == user.age;
                } else {
                    return false;
                }
            }

            @Override
            public final int hashCode() {
                int hashcode = 17;
                hashcode = hashcode * 31 + (int)this.id;
                hashcode = hashcode * 31 + this.age;
                return hashcode;
            }
        }
        Map<User, String> map = new HashMap<>();
        User user1 = new User(1l,"hu", 28);
        User user2 = new User(1l,"hu", 28);
        map.put(user1, "hujt");
        System.out.println(user1.equals(user2));
        System.out.println(map.get(user2));

    }
上面程式碼返回true,“hujt",達到預期的結果。如果我們用同一個物件例項去put(user1, "XXX"),再用get(user1)返回的結果就是“XXX”,效果一樣。