【JDK源码阅读10-util】Set接口---HashSet

HashSet

一、类继承关系

  
public class HashSet<E>
                  extends AbstractSet<E>
                  implements Set<E>, Cloneable, Serializable
特点:
         1   HashSet实现Set 接口,由哈希表支持,底层是一个HashMap 实例
      2   插入元素顺序是无序的(由HashCode决定)
      3   不能添加重复的元素(即添加重复元素的时候会返回false,添加失败),允许添加null 元素。 
      4  HashSet是非线程同步的,实现同步的操作:         
        Set set = Collections.synchronizedSet(new HashSet());

二、HashSet 两个重要特性


    学习HashSet一定要清楚,其底层实现是一个HashMap实例,其所有的添加删除等基本操作都是调用HashMap实例中的方法进行操作的;
 这也就能理解HashSet的两个重要特性:无序性  和 不可重复性。
          即:

 HashSet 底层是由HashMap 实现的:即将Set集合的值作为Map集合的键,HashMap中 值不用考虑,可以设置个固定值PRESENT。对应关系如下:

map集合的键《《---》》》Set集合

而我们指知道,map集合的键是不能重复的(键key可以为null),这也就是解释了HashSet集合允许有null元素集合,且不能有重复元素的原因  

三、HashSet 中add()方法在算法题中的应用

HashSet 最有用的方法就是set.add(Element e)这个方法,可用来判断原集合中是否含有将要添加的元素e

 如果含有重复元素e,就返回false,添加失败;否则添加成功返回true


The fucking source code:

package jdk_util;

import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

public class JDK_HashSet {

	/**
	 * HashSet底层由于是HashMap实现的,即HashSet集合的值被当做HashMap的键,HashMap值设为定值:伪值--PRESENT
	 * 由于HashMap键允许为null,不能重复;所以HashSet集合允许有null元素,且不能有重复元素。
	 * 
	 *HashSet类是Set的主要实现类,由哈希表支持(实际上是一个HashMap实例)。它不能确保set集合中元素的迭代顺序;
	 *特别是不能确保顺序不变,HashSet允许null元素存在。

	 *假定哈希函数正确地将这些元素分配在桶中。
	 *迭代set集合需要的时间与HashSet实例的大小(元素数量)和底层HashMap实例(桶的数量)的"容量"成比例。
	 *因此,如果迭代性能很重要,就不要将集合初始容量设置太高(即加载因子设置太低)。
	 *
	 * 注意,HashSet是非同步,即非线程安全的。  
	 * 			Set s = Collections.synchronizedSet(new HashSet(...));
	 * 
	 * HashSet 底层是由HashMap 实现的:即将Set集合的值作为Map集合的键。
	 *     而我们指知道,map集合的键是不能重复的(键值可以为null),
	 *     map集合的键《《---》》》Set集合,
	 *     这也就是解释了HashSet集合允许有null元素集合,且不能有重复元素的原因
	 *    而值不用考虑,可以设置个固定值PRESENT
	 */

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

	    //创建一个HashMap 对象。
	    //transient防止对象被序列化;
	    //由此可看出 HashSet 底层是由HashMap 实现的:即将Set集合的值作为Map集合的键。
	    private transient HashMap<E,Object> map;

	    //用来填充HashMap的值对象(键对象由set集合填充)
	    private static final Object PRESENT = new Object();

	    //构造器:创建空的Set集合;
	    //底层的HashMap的初始化容量为16,加载因子为0.75
	    public HashSet() {
	        map = new HashMap<>();
	    }

	    //创建包含集合对象c的Set集合;
	    //底层的加载因子仍然是0.75;大小不小于16
	    public HashSet(Collection<? extends E> c) {
	        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
	        addAll(c);
	    }

	    //指定初始化容量和加载因子的HashSet;底层即为HashMap
	    public HashSet(int initialCapacity, float loadFactor) {
	        map = new HashMap<>(initialCapacity, loadFactor);
	    }

	    //指定初始化容量HashSet;用HashMap实现,加载因子的默认为0.75
	    public HashSet(int initialCapacity) {
	        map = new HashMap<>(initialCapacity);
	    }

	    //构建一个链式hashSet,指定初始化容量和加载因子;
	    //这个构造器只用来提供给LinkedHashSet使用;
	    //底层的HashMap实例是一个具有指定初始化容量和加载因子的LinkedHashMap
	    //dummy:?
	    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
	        map = new LinkedHashMap<>(initialCapacity, loadFactor);
	    }

	    //返回set集合迭代器,顺序是无序的
	    //底层实现是返回map集合的keySet集合的遍历(再次证明hashset集合值作为hashmap集合的键)
	    public Iterator<E> iterator() {
	        return map.keySet().iterator();
	    }

	    //返回set集合的大小;
	    //实际上是map集合的大小
	    public int size() {
	        return map.size();
	    }

	    //判断是否非空 
	    public boolean isEmpty() {
	        return map.isEmpty();
	    }

	    //判断是否包含元素o,实际上是在map集合中keySet寻找,是否有这样的键值o
	    public boolean contains(Object o) {
	        return map.containsKey(o);
	    }

	    //添加,若待添加的值存在,就返回false
	    //原理:调用的是hashMap.put(key,value);
	    //hashMap中添加元素是先判断键所在位置是否有元素,有的话就将新值value覆盖旧值,并返回旧值;
	    //                      若所在位置没有元素,就加入value值到指定key处,并返回null
	    //这也就解释了hashset中不能有重复元素的原因,因为不能添加重复元素
	    public boolean add(E e) {
	        return map.put(e, PRESENT)==null;//若添加后值==null,说明原来key处无值,就返回true
	        							       //否则返回false
	    }

	    //移除指定元素o
	    public boolean remove(Object o) {
	        return map.remove(o)==PRESENT;
	    }

	    //清空集合
	    public void clear() {
	        map.clear();
	    }

	    //浅复制
	    public Object clone() {
	        try {
	            HashSet<E> newSet = (HashSet<E>) super.clone();
	            newSet.map = (HashMap<E, Object>) map.clone();
	            return newSet;
	        } catch (CloneNotSupportedException e) {
	            throw new InternalError();
	        }
	    }

	    //序列化:将HashSet实例保存在流中
	    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 (E e : map.keySet())
	            s.writeObject(e);//集合的值
	    }

	    //反序列化:读取流中的集合值
	    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<E,Object>(capacity, loadFactor) :
	               new HashMap<E,Object>(capacity, loadFactor));
	        // Read in size
	        int size = s.readInt();
	        // Read in all elements in the proper order.
	        for (int i=0; i<size; i++) {
	            E e = (E) s.readObject();
	            map.put(e, PRESENT);
	        }
	    }
	}

}















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值