理解 | hashCode()方法重写以及HashSet元素的唯一性

目录

Object的hashCode()方法:

引用类型的hashCode()方法:

基本类型String的hashCode()方法:

引用类型的HashSet存储:

String基本类型的HashSet存储:


说hashCode()方法之前先来介绍一下什么是哈希值:

哈希值:是数据存储的逻辑地址,类似于房间的门牌号。

Object的hashCode()方法:

JDK源吗:

    public native int hashCode();

跟句源吗知道这仅仅是一个类似于接口形式的方法,子类可以选择去重写该方法;

引用类型的hashCode()方法:

创建User.java:(没有重写hashCode()方法)

public class User {
    private String username;
    private String password;

    public User() {
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

创建测试类test.java:

        User user1=new User("tom","11");
        User user2=new User("tom","11");
        System.out.println(user1.hashCode());//1163157884
        System.out.println(user2.hashCode());//1956725890

可见两个对象虽然值都相同,但是哈希值是不同的;

基本类型String的hashCode()方法:

JDK源码:

        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;
        }

根据源码可以看出相同的对象的哈希值是相同的,我对结论进行验证:

        String s1="11";
        String s2=new String("11");
        String s3="11";
        System.out.println(s1.hashCode());//1568
        System.out.println(s2.hashCode());//1568
        System.out.println(s3.hashCode());//1568

根据输出证明了我的猜想是对的。(字符串值相同的元素的哈希值是一样的)

 

好了,有了前边的例子进行铺垫,我下边对HashSet的存储进行了研究

引用类型的HashSet存储:

在测试类test中添加以下代码:

        HashSet<User> hashSet=new HashSet<User>();
        hashSet.add(user1);
        hashSet.add(user2);
        for (User u : hashSet){
            System.out.println(u.getUsername()+"*********"+u.getPassword());
        }

输出:tom*********11
           tom*********11

可见HashSet没有重写Object的hachCode()方法而导致user1和user2的哈希值不同,一起存入哈希表中。

所以想要保证哈希表中元素的唯一性,需要重写hachCode()方法,然而根据add()的JDK源码:

    //HashSet
    private transient HashMap<E,Object> map;
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    //HashMap
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
       final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

如果想要保证元素的唯一性,当创建了相同的元素添加到哈希表中,需要把哈希值设置成相同的,而且还要重写equals()方法;

在User.java中自动重写hachCode()和equals()方法:

    @Override
    public int hashCode() {
        return Objects.hash(username, password);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return Objects.equals(getUsername(), user.getUsername()) &&
                Objects.equals(getPassword(), user.getPassword());
    }

又根据 Objects.hash源码发现:

    //Objects.java:
    public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }

查看Arrays.hashCode方法:

    public static int hashCode(Object a[]) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());

        return result;
    }

通过该方法使得值相同的对象拥有了相同的哈希值。又根据equals()判断值是否相等,,保证了HashSet元素的唯一性。

此时,再次运行测试类test.java:

输出:tom*********11

试试证明了开始的猜想是对的。

 

String基本类型的HashSet存储:

        String s1="11";
        String s2=new String("11");
        String s3="11";
        System.out.println(s1.hashCode());//1568
        System.out.println(s2.hashCode());//1568
        System.out.println(s3.hashCode());//1568
        if (s1.equals(s2)){
            System.out.println("相等");
        }
        else {
            System.out.println("不相等");
        }//相等
        //比较地址值
        System.out.println(s1==s2);//false
        System.out.println(s1==s3);//true
        HashSet<String> s=new HashSet<String>();
        s.add(s1);
        s.add(s2);
        s.add(s3);
        for (String a : s){
            System.out.println(a);
        }
        //11

由于String类即重写了hashCode()方法和equals()方法,所以保证了添加到哈希表中的元素的唯一性。

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值