HashSet中的add方法

1,对HashSet新建对象的分析

HashSet<String> set=new HashSet<String>();
创建一个HashSet类的对象,new HashSet<String>()调用了HashSet中的无参构造方法
 public HashSet(){
 	 map=new HashMap<>();
 }

实质又调用了HashMap中的无参构造方法
 public HashMap() {
   	 this.loadFactor = DEFAULT_LOAD_FACTOR; 
 }

2,hash方法

因为在对add方法进行分析时,会用到hash方法,先分析hash方法

01,传入String类型的值:比较内容是否相同,相同的内容返回值是相同的

	public class Test(){
	 static int hash(Object key) {
	        int h;
	        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
	 }	//	hash方法:返回值为int类型的,其中又调用了hashCode方法
	 
	 public static void main(String[] args) {
	
	int hash=hash("tom");		//hash方法中传入String类型的值后,key不为空,执行三目运算中冒号后边部分,调用hashCode方法
	System.out.println(hash);
	hash=hash(new String("tom"));
	System.out.println(hash);
	}
	
输出为:
115027
115027

其中String类中的hashCode方法如下,经过运算返回int类型的值

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

02,传入int类型的值,比较数值,数值相同返回值相同。

public class Test(){
 static int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }	//	hash方法:返回值为int类型的,其中又调用了hashCode方法
 
 public static void main(String[] args) {
	 hash=hash(15);
	 System.out.println(hash);
	 hash=hash(15);
	 System.out.println(hash);
 }
 
 输出为:
 15
 15

其中Integer 类中的hashCode方法如下,经过运算返回int类型的值,传入返回的值。

public static int hashCode(int value) {
    return value;
}

03,对传入的自定义类的对象:比较地址,返回int类型的值

public class Test(){
 static int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }	//	hash方法:返回值为int类型的,其中又调用了hashCode方法
 
 public static void main(String[] args) {
 	hash=hash(new Student());
 	System.out.println(hash);
 	hash=hash(new Student());	//两个地址:运算结果不同
 	System.out.println(hash);
	}
 }

//自定义类:
class Student{

}

输出为:
5433712
2430314

自定义类中的HashCode说明

因为自定义类中没有重写父类的hashCode方法,所以该类中的hashCode方法默认继承自父类Object,经过运算返回int类型的值。

//Object类中定义的hashCode方法:
public native int hashCode();

3,HashSet中add方法是怎样执行的

HashSet<String> set=new HashSet<String>();	
	set.add("tom");		
	set.add("tom");
	set.add("lucy");
	
代码分析:每次执行代码set.add();时调用add方法,传入的e的值为tom,再调用HashMap中的put方法,传入的key的值为tom,(HashSet存储的数据实质存在了HashMap的key),再调用putVal方法(其中的hash(key)返回int类型的数值)
//HashSet中的add方法
	public boolean add(E e) {
	        return map.put(e, PRESENT)==null;
	}
//HashMap中的put方法
	public V put(K key, V value) {																			
	        return putVal(hash(key), key, value, false, true);
	}
//HashMap中的putVal方法
	//详述putval方法
	
//第一次存储tom时
 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {		//Node<K,V>[] tab;定义了一个集合,Node<K,V> p;定义集合中的值,table是全局变量,未初始化之前默认值为null.
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)		//该if语句执行,将值赋给tab后,前半部分返回值为true,因为逻辑或遇真短路(后半部分不再运算),所以if条件满足,执行下一行代码。
        n = (tab = resize()).length;	//resize()方法将集合重置,重置后有了长度,且重置后的长度为16,因为调用了HashMpa中定义的常量【static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16】,然后执行下一行代码。
    if ((p = tab[i = (n - 1) & hash]) == null)		//i = (n - 1) & hash语句得到一个位置下标,对于p = tab[i = (n - 1) & hash],第一次存储集合为空,所以if条件成立,执行下一行代码添加tom,然后跳出该if语句。
        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) 
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { 
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;			//跳出if语句后执行该语句,实现将tom存储。
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

	//关于resize()方法
	 final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;	//table是null,返回值为newTab,是一个重置的有长度的集合。
	.......
	 if (oldTab != null) {

	 }
        return newTab;
	 }

//第二次存储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;
    if ((tab = table) == null || (n = tab.length) == 0)		//table不为空,n = tab.length也不等于0,执行下一个if语句.
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)	//因为同一个String类型的值,hash相同,n为固定的长度,所以获取的下标与第一次相同,此时位置不为空,所以不添加到tab[i],执行else语句
        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))))		//两次都是tom,所以hash相同,p.hash == hash返回true,((k = p.key) == key || (key != null && key.equals(k)))返回true,执行e=p;语句,执行下一个if语句
            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) { 			//此时e不为空,所以一定会执行return oldValue;
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;		//关掉方法,不存储数据。
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

//第三次时:存储lucy。

 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不为空,n = tab.length也不等于0,执行下一个if语句.
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)	//lucy调用hash方法后得到得值与tom不同,所以经过运算之后得到一个i,且此时该下标处为空,将lucy添加进去,跳出这个if语句。
        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)
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { 
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }			
    ++modCount;		//跳出if语句后执行该语句,实现将lucy存储。
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

//常量池机制:在常量池中存储值时,先判断常量池中是否存在该值,如果存在将不再进行添加,不存在时才添加。	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值