情况1添加一个直接赋值的字符

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

进入HashSet add方法。返回值为boolean。对象map调用put方法。看put方法的返回值是否为空。来返回true,false

public HashSet() {
    map = new HashMap<>();
}    
    private transient HashMap<E,Object> map;

点入map对象,发现map是由HashMap创建的对象。所以HashSet的add方法实质上是调用的HashMap的put方法。

private static final Object PRESENT = new Object();

而map的put方法需传入两个参数,e为调用add时传入的,PRESENT为源代码中定义的常量

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

进入put方法。参数key为add方法时添加的数据,value为PRESENT常量。有返回值为V类型的值 。进入hash方法

  static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

是个静态final方法。传入key参数。int一个h来接收key的hashCode,hashCode方法有特定的公式来计算后返回数据的hashCode值,hash方法基于hashCode经过运算后返还。我这里把hash(key)理解成一个key的特殊地址。

回到put方法。进入putVal方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) //此为putVal方法的定义{
    Node<K,V>[] tab; Node<K,V> p; int n, i;//创建一个名为tab的Node数组。
    if ((tab = table) == null || (n = tab.length) == 0)//table为一个全局变量,由Node数组创建是一个空数组。//tab和table指向同一个地址,所以(tab=table)==null为true。
        n = (tab = resize()).length;//resize()的返回值默认长度为16.
    if ((p = tab[i = (n - 1) & hash]) == null)//n为16,16减一为15.按位与hash值不为空所以为ture
        tab[i] = newNode(hash, key, value, null);//key为add加的数据,value为一个常量(PRESENT)和对应的hash赋值 数组tab[i];
    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;//执行完上一个if后,直接在这里返回null。所以map.put()==null成立。返回true 添加成功}

情况2 第二次直接赋相同值时

 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)//第二次赋值时,table为16不为null.
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)//n还是16,因为第二次赋的值相同所以hash值也相同,所以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;//因为两次的值hash值一样。所以将p中的第一次的值赋值给e;
        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//e中有第一次的值。所以不为null,所以为true
            V oldValue = e.value;//将e的value传给 oldvalue
            if (!onlyIfAbsent || oldValue == null)//onlyIfAbsent为put 方法传进来的false.前面有个!所以为true。
                e.value = value;//hashSet add添加的数据对应hashMap的key。所以value对hashSet没什么意义。
            afterNodeAccess(e);
            return oldValue;//返回值oldValue,结束该方法。
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
Posted on 2021-05-12 18:02  lezai0514  阅读(211)  评论(0)    收藏  举报