HashSet add() 方法底层调用HashMap时,存入的value为什么不是null?

    技术2022-08-01  73

    看jdk源码,看到 LinkedHashSet时,说底层调用的LinkedHashMap进行存放数据,点入LinkedHashSet的add方法看了一下,看到如下代码:

    /** * Adds the specified element to this set if it is not already present. * More formally, adds the specified element <tt>e</tt> to this set if * this set contains no element <tt>e2</tt> such that * <tt>(e==null ? e2==null : e.equals(e2))</tt>. * If this set already contains the element, the call leaves the set * unchanged and returns <tt>false</tt>. * * @param e element to be added to this set * @return <tt>true</tt> if this set did not already contain the specified * element */ public boolean add(E e) { return map.put(e, PRESENT)==null; }

    其中的PRESENT 为类的一个私有Object对象。

    private static final Object PRESENT = new Object();

    这就很值得研究一下,为什么add()方法中,存入的value不是null?正常来说,存入null的效率和空间占用是否会更好一点?

    继续往下看,看map.put()方法的实现:

    /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }

    putval()方法如下:

    /** * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ 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; }

    实现不需要仔细看,先看他的关于return的注释:

    * @return previous value, or null if none

    意思就是,返回当前值,如果值不存在,为第一次添加该key的话,返回null。

    所以,如果前边add时,添加的value为null,那返回的话,就不能区分时第一次添加返回的空值null,还是重复添加时,返回的已有值,只是这个值为null,但是,添加的如果是一个Object,就可以避免这个问题了。

    源码的每一步实现,都是有道理的。 

     

    Processed: 0.009, SQL: 9