JavaにおけるHashSetクラスadd方法を詳述する(2):重複要素を追加する。

21605 ワード

同じ要素を2つ追加します。
import java.util.HashSet;

public class Test {

	public static void main(String[] args) {
		HashSet<String> names = new HashSet<String>();
		names.add("Jim");
		names.add("Jim");//HashSet         
}
add法の繰り返し要素に対する処理を解析した。addソースコード:
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
	}
HashMapのput方法:
public V put(K key, V value) {//K key Jim,V value   PRESENT
        return putVal(hash(key), key, value, false, true);
    }
hash(key):
static final int hash(Object key) {
     int h;
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
     //  key null,   0,    key    
} 
putVal方法:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

	transient Node<K,V>[] table;//table=null

    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;
    }
二番目の要素を追加するので、テーブルは最初の追加時に値を割り当てられます。したがって、グローバル変数テーブルはnullではないので、最初の判断条件はfalseです。また、2つの要素が重複すると、hashcodeの値が同じであるため、tab[i=(n-1)&hash]は最初に追加された値がnullではないため、2つ目の判断条件はfalseであり、elseに入る。
  if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
最初の追加時にアドレスをpに割り当てたので、p.hashは最初の要素のhash値です。p.hash==hashは同じです。また、2つの要素は常量池のアドレスが同じなので、(k=p.key)=keyとなります。
 if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
                }
eはnullではない条件はtrueです。putValメソッドを呼び出した時にonlyIfAbsentに定義があります。
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)//onlyIfAbsent false
onlyIfAbsentはfalseである。onlyIfAbsentはtrueで、trueと判断します。
if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
return oldValue;
最後に最初の要素のvalueを返します。nullではないので、貯蔵に失敗しました。