深入集合框架之HashSet源码剖析

HashSet实现了Set接口,也就是说它存储的元素是无重复的。

通过源码分析我们可以发现HashSet就是HashMap的一个实例。

因为在HashMap中的键是不能重复的,我们可以把HashSet想象成HashMap中的键,而且事实也就是如此。




public class HashSet
   
   
    
    
    extends AbstractSet
    
    
     
     
    implements Set
     
     
      
      , Cloneable, java.io.Serializable
{
    
    static final long serialVersionUID = -5024744406713321676L;

    //底层使用HashMap来保存HashSet的元素
    private transient HashMap
      
      
       
        map;

    // 虚拟的对象,用来支持HashMap
    private static final Object PRESENT = new Object();

    /**
     * 创建一个新的空的HashSet,用来支持HashMap,默认初始容量和HashMap的一样
     * 初始容量16,加载因子0.75
     */
    public HashSet() {
	map = new HashMap
       
       
         (); } /** * 将一个set集合存入HashSet中, * 类似HashMap中的将一个子集放入已有的HashMap内 * 底层调用HashMap实现 */ public HashSet(Collection 
         c) { map = new HashMap 
        
          (Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /** * 构造一个空的HashSet,产生的是一个HashMap实例, * 其值便是成员变量中的Object,并且指定容量和加载因子 * */ public HashSet(int initialCapacity, float loadFactor) { map = new HashMap 
         
           (initialCapacity, loadFactor); } /** * 构造一个空的HashSet,指定容量,加载因子为默认的 * 如果看过HashMap的话,就会知道这个在HashMap中会默认调用上面那个构造函数 */ public HashSet(int initialCapacity) { map = new HashMap 
          
            (initialCapacity); } /** * 此构造方法是默认的,只能被LinkHashSet访问,表示构造一个空的LinkHashSet, * 我们可以看出来,这也是以HashMap作为基础。 */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap 
           
             (initialCapacity, loadFactor); } /** * 返回这个set的迭代器,可以看出这个底层是调用了HashMap的 * keySet方法返回所有的Key */ public Iterator 
            
              iterator() { return map.keySet().iterator(); } /** * 返回HashSet的实际大小,也就是一共有多少个键 */ public int size() { return map.size(); } /** * 当HashSet为空时返回true */ public boolean isEmpty() { return map.isEmpty(); } /** * 如果Set中包含了指定的元素,返回true. * 更确切的说是:当且仅当此 set 包含一个满足 * (o==null ? e==null : o.equals(e)) 的 e 元素时,返回 true。 * */ public boolean contains(Object o) { return map.containsKey(o); } /** * 往HashSet中添加一个值,可以发现当添加的值已经存在时, * 也就是两者的key的hashcode值相等并且key通过equals比较也相等, * 用新的value覆盖旧的value,但是key的值不会有什么变化 */ public boolean add(E e) { return map.put(e, PRESENT)==null; } /** * 移除的对象和以前放入的对象一致,则移除成功 */ public boolean remove(Object o) { return map.remove(o)==PRESENT; } /** * 清空HashSet */ public void clear() { map.clear(); } /** * 返回HashSet的一个浅拷贝,调用的是HashMap的浅拷贝, * 因为只拷贝的是里面对象的引用,具体的引用没有拷贝 */ public Object clone() { try { HashSet 
             
               newSet = (HashSet 
              
                ) super.clone(); newSet.map = (HashMap 
               
                 ) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } } /** *将map的容量、加载因子、大小以及每个值都序列化 */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out HashMap capacity and load factor s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); // Write out size s.writeInt(map.size()); // Write out all elements in the proper order. for (Iterator i=map.keySet().iterator(); i.hasNext(); ) s.writeObject(i.next()); } /** *反序列化时如果类型是LinkedHashSet则用LinkedHashMap进行处理 * 否则用HashMap进行处理 */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in HashMap capacity and load factor and create backing HashMap int capacity = s.readInt(); float loadFactor = s.readFloat(); map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap 
                
                  (capacity, loadFactor) : new HashMap 
                 
                   (capacity, loadFactor)); // Read in size int size = s.readInt(); // Read in all elements in the proper order. for (int i=0; i 
                   
                  
                 
                
               
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   

接下来我们具体分析一个构造函数:

public HashSet(Collection
     
      c) {
	map = new HashMap
     
     
      
      (Math.max((int) (c.size()/.75f) + 1, 16));
	addAll(c);
    }
    
//注意到这个addAll方法,深入进去后,发现是AbstractCollection类中的
   /**
     *这里的这个迭代器就是HashSet中我们定义的那个迭代器
     *所以就是遍历参数中的set,添加到我们原有的set中
     */
    public boolean addAll(Collection
      
       c) {
	boolean modified = false;
	Iterator
      
       e = c.iterator();
	while (e.hasNext()) {
	    if (add(e.next()))
		modified = true;
	}
	return modified;
    }

    public Iterator
      
      
       
        iterator() {
    	return map.keySet().iterator();
    }
    
    /**
     * 我们注意到add中的代码是跟null进行比较
     * 这是因为我们在HashMap中添加元素时,如果key不存在就返回null,否则就返回旧值
     */
    public boolean add(E e) {
    	return map.put(e, PRESENT)==null;
    }
    
    
    
      
      
     
     


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值