HashSet如何保证元素的唯一性?

HashSet如何保证元素的唯一性?

                                                               
                                                                                             
HashSet存储自定义对象来保证唯一性。                                                          
需求:如果两个对象的成员变量值都相同,则为同一个对象。也就是同一个元素。                     
                                                                                             
String:                                                                                      
		name,age                                                                                
                                                                                             
按照我们的想法,Set集合应该给我们去掉了重复的元素。                                          
但事实上,这个元素还存在,这是为什么呢?                                                      
		因为两个对象的成员变量值相同,这两个对象未必是同一个对象。                              
		只不过我们的需求认为他是同一个对象。                                                    
		而我们又知道要比较对象的成员变量值,就必须重写equals()。                                
                                                                                             
我们重写了equals()后,并没有实现去掉重复元素。那么,为什么呢?                                
		我们做一个简单的检测,看equals()方法有没有被执行。                                      
                                                                                             
通过一个简单的测试,我们发现程序并没有去执行equals(),这又是为什么呢?                        
		看add方法的源码。                                                                       
最终我们发现了罪魁祸首:                                                                     
		if (e.hash == hash && ((k = e.key) == key || key.equals(k)))                            
		{                                                                                       
			就没有添加到集合中。                                                                
		}                                                                                       
		                                                                                        
		这个判断分为左边和右边两部分:                                                          
		左边:                                                                                  
			e.hash == hash                                                                      
			左边比较的是哈希值,而对象的哈希值一般来说肯定不一样。                              
			所以,左边永远是false。                                                             
			所以,右边永远执行不了。                                                            
                                                                                             
		右边:                                                                                  
			((k = e.key) == key || key.equals(k))                                               
			分为左边和右边:                                                                    
			左边:                                                                              
				//k是s1的地址值                                                                 
				//key是s2的地址值                                                               
				(k = e.key) == key                                                              
                                                                                             
				由于不同对象的地址值肯定不一样,所以,左边一般肯定是false。                     
			右边:                                                                              
				key.equals(k)                                                                   
                                                                                             
HashSet集合的底层数据结构是哈希表。                                                          
		哈哈希表的存储依赖两个方法:                                                            
			hashCode()和equals()。                                                              
		执行顺序:                                                                              
			首先判断对象的哈希值是否相同,                                                      
				如果相同,就继续执行equals()方法。                                              
					如果equals()方法返回true,说明元素重复了。就不添加。                        
					如果equals()方法返回false,说明元素没有重复的,就添加到集合中。             
				如果不同,就直接添加到集合中。                                                  
                                                                                             
以后你看到HashXxx结构的集合,要知道,这种结构底层是哈希表。(是一个元素是链表的数组)          
它依赖hashCode()和equals()方法。                                                             
		自动生成即可。                                                                          

HashSet保证元素唯一性原理图解

在这里插入图片描述

HashSet保证元素唯一性的源码

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

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

class HashMap {
    
    
	public V put(K key, V value) {
    
    
		//key -- e -- s1,s2
		//value -- new Object()
	
		//哈希表是否为空。
        if (table == EMPTY_TABLE) {
    
    
            inflateTable(threshold);
        }
        
        //不执行这个if判断。因为我们的数据不是null。
        if (key == null)
            return putForNullKey(value);
        
        
        int hash = hash(key); //可以理解为返回该对象的哈希值。
        					  //s2的哈希值
        
        int i = indexFor(hash, table.length); //在哈希表中查找元素出现的位置。
        
        //第一次,哈希表没有元素,所以,第一次存储元素,不需要执行这里。
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
    
    
            Object k;
            //e -- s1
            //this -- s2
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    
    
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++; //统计++
        addEntry(hash, key, value, i); //添加元素。
        return null;
    }
    
    //这个hash方法的返回值和k的hashCode()方法相关。
    final int hash(Object k) {
    
    
    	//k -- s1
    	
        int h = hashSeed;
        if (0 != h && k instanceof String) {
    
    
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode(); //这个跟对象的hashCode()相关。

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35655602/article/details/106710623