Java HashSet类中的add方法

public class Test{	
	public static void main(String[] args){
		HashSet<String>set = new HashSet<>();
		//调用HashSet无参构造方法,为HashSet类中HashMap类型的全局变量赋值
 		set.add("Tom");//map.put(e,PRESENT),HashSet添加元素实质是使用HashMap的key
 		set.add("Tom");//这里我们添加两次,在下面将会详细解释添加两次相同数据的具体过程
	}
}

我们先在eclipse中用ctrl+左键点进add方法
在这里插入图片描述
再进一步点进put方法:
在这里插入图片描述
我们可以看到put方法返回的是putVal方法的返回值。
这里的hash(key)返回当前数据的hashCode码,就为下面putVal方法的hash值
在这里插入图片描述
接下来我们来看putVal方法,第一次添加Tom:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //这里的table是个数组类型的全局变量,初始值为null,所以我们第一次添加对象时tab = table都是null
        if ((tab = table) == null || (n = tab.length) == 0)
  	   //这里我们只需要知道,这里的resize(),是给table赋值,并且返回这个值,所以这里tab = table,n就是这个数组的长度
            n = (tab = resize()).length;
            //这个经过resize的数组,是一个长度为16,每一个元素都为null的数组,所以这个条件判断成立,tab[i]赋值成功,直接转到下面
        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);
        //这里最后返回null,这里返回null的话,add方法就返回true,添加成功!
        return null;
    }

然后我们来看第二次传入Tom:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //这里条件判断为false,因为已经添加了一个Tom在里面,代码已经运行过了一遍所以table不为null,n也不等0了,所以n的值不会改变,接着往下走
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
            //n的值既然没有改变,那么这里i也没有改变,这里的tab[i]已经被赋值过了,所以不为null,进入下方else
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //这里后面的hash就是String类型数据Tom的hashCode码,这里的p为tab[i],即添加的Tom,p.hash也是String类型数据Tom的hashCode码,两个码相同为true。再看后面,k和key都是"Tom",但地址不同,k=p.key为false;key!=null为true,key.equals(k)为trueh以整个条件判断为true
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //于是这里e = p,这些同级的条件判断就跳过了
                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(hac, key, value, null);e                       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;
                }
            }
            //由于e = p,p = tab[i],所以这里e不为null,条件判断为true
            if (e != null) {// existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                //这里返回不为null,所以add方法为false,添加失败
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

接下来,我们来使用HashSet的add方法来添加自定义类:

public class Test {e
 	public stateic void main(String[] args) {
  		HashSet<Student> set  = new HashSet<>();
  		set.add(new Student("Tom"));
  		set.add(new Student("Tom"));
  		System.out.println(set.size());
 	} 
}
class Student{
 	String id;
 	public Student(String id) {
  		this.id = id;
 	}
 	//这里的两个方法重写,我们下面分开来讨论
 	@Override
 	public int hashCode() {
  		return this.id.hashCode();
 	}
 	@Override
 	public boolean equals(Object obj) {
  		Student stu = (Student)obj;
  		return this.id.equals(stu.id);
 	} 
h}

我们先看添h加第一个new Student(“Tom”);的情况:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //这里table为null,n为0,同上
        if ((tab = table) == null || (n = tab.length) == 0)
        //这里n有了一个值,table也变为了长度为16的数组,每个元素都是null
            n = (tab = resize()).length;            
        //这里hash是用Object中的hashCode方法得出的,不过这里tab[i]还是为null
        if ((p = tab[i = (n - 1) & hash]) == null)
    c        c//这里tab[i]赋值成功,跳过同级条件判断
            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);
        //返回null,则add方法返回true,添加成功
        return null;
    }

接下来,关键的来了!我们再添加一个new Student(“Tom”),先说没有重写方法的情况

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //还是先走这里,这回table不为null了,长度为16,这个条件判断为false
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //这里的hash是一个关键哦,如果Student类中没有重写任何方法,那么这里的hash为Object中hashCode方法的返回值,为调用该方法数据的地址,所以这里==null是true!
        if ((p = tab[i = (n - 1) & hash]) == null)
            //所以这里tab[i]赋值成功,跳过同级条件判断
            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,add方法return为true,添加成功
        return null;
    }

然后我们讨论在重写hashCode方法没有重写equals方法的情况下,添加第二个new Student(“Tom”):

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //还是先走这里,为false
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //由于我们重写了hashCode方法,所以这里的i值和第一次添加时一样,这里条件判断也为false,走进下面else中
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //先走这,重写了hashCode,所以p.hash ==hash为true
            //下面k == key为false,因为这是判断的地址,再看这个equals:如果我们没有重写equals方法,那么这里用的是Object中的equals方法,返回为false,因为比较的是地址,那就继续往下走
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;              
            //这里也为false,继续往下
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            //走到这里
            else {
                //这是个无限循环
                for (int binCount = 0; ; ++binCount) {
                    //这里p.next为null,我们只添加了一个Student对象在里面,他的下一个位置一定为null
                    if ((e = p.next) == null) {
                        //这里给p.next赋值成果
                        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;
                }
            }
            //e = p.next(),那时p.next还没赋值过,为null,这里e为null,为false
            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;
    }

最后,hashCode和equals方法都重写的情况

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //false,不多解释
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //重写了hashCode为false
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //走这里,重写了hashCode所以p.hash == hash为true,key!=null为true,重写了equals方法所以key.equals(k))为true,整个条件判断为true
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //所以e = p,跳过同级条件判断
                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;
                }
            }
            //e = p,所以这里为true
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                //返回不为null,添加失败!
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值