2019独角兽企业重金招聘Python工程师标准>>>
本文简单分析一下JDK1.7的HashSet源码,看一下其内部的结构以及典型方法的实现~
HashSet的内部结构
HashSet类继承AbstractSet,实现Set接口、实现了Cloneable接口以及序列化Serializable接口~如:
public class HashSet
extends AbstractSet
implements Set, Cloneable, java.io.Serializable
所以,其具备Set的基本特性之外,也支持克隆以及序列化相关能力。
public class HashSet
extends AbstractSet
implements Set, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
从上述代码可以看出,HashSet内部使用了HashMap,还有一个static final定义的对象 PRESENT~ 具体map和PRESENT是如何使用的? 我们在接下来的代码实现分析中介绍~
构造函数的实现
默认构造函数源代码
/**
* Constructs a new, empty set; the backing HashMap instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
HashSet默认构造函数将构建一个容量为16,加载因子为0.75的HashMap。如果想对HashMap的实现有更多的了解,可以参考之前的博文《HashMap的实现原理浅析》。
包含容量和加载因子的构造函数
/**
* Constructs a new, empty set; the backing HashMap instance has
* the specified initial capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
包含集合参数的构造函数
/**
* Constructs a new set containing the elements in the specified
* collection. The HashMap is created with default load factor
* (0.75) and an initial capacity sufficient to contain the elements in
* the specified collection.
*
* @param c the collection whose elements are to be placed into this set
* @throws NullPointerException if the specified collection is null
*/
public HashSet(Collection extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
方法iterator的实现
源代码
/**
* Returns an iterator over the elements in this set. The elements
* are returned in no particular order.
*
* @return an Iterator over the elements in this set
* @see ConcurrentModificationException
*/
public Iterator iterator() {
return map.keySet().iterator();
}
从构造函数可以看出,HashSet内部封装使用了HashMap,其iterator、size、isEmpty的方法都是通过调用HashMap来实现的。
就像上述的iterator方法~, 其就是使用了map.keySet()的iterator方法~
方法size的实现
源代码
/**
* Returns the number of elements in this set (its cardinality).
*
* @return the number of elements in this set (its cardinality)
*/
public int size() {
return map.size();
}
同样,个数size()方法也是调用map.size()方法~
方法isEmpty的实现
源代码
/**
* Returns true if this set contains no elements.
*
* @return true if this set contains no elements
*/
public boolean isEmpty() {
return map.isEmpty();
}
HashSet的isEmpty()方法也是直接调用了 map.isEmpty()实现~
方法contains的实现
源代码
/**
* Returns true if this set contains the specified element.
* More formally, returns true if and only if this set
* contains an element e such that
* (o==null ? e==null : o.equals(e)).
*
* @param o element whose presence in this set is to be tested
* @return true if this set contains the specified element
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
HashSet的contains方法直接调用了map的containsKey(o)方法实现~
方法add的实现
源代码
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element e to this set if
* this set contains no element e2 such that
* (e==null ? e2==null : e.equals(e2)).
* If this set already contains the element, the call leaves the set
* unchanged and returns false.
*
* @param e element to be added to this set
* @return true if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
从上述代码可以看出,HashSet的add方法,其实也是调用了map的put方法,只是,这里map使用的值是static final定义的Object对象,也即PRESENT~
import java.util.HashSet;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Set nums = new HashSet<>();
nums.add("hello");
nums.add("java");
nums.add("Eric");
nums.add("John");
nums.add("LiLei");
nums.add("Wang");
nums.add("Zhang");
System.out.println(nums);
}
}
方法remove的实现
源代码
/**
* Removes the specified element from this set if it is present.
* More formally, removes an element e such that
* (o==null ? e==null : o.equals(e)),
* if this set contains such an element. Returns true if
* this set contained the element (or equivalently, if this set
* changed as a result of the call). (This set will not contain the
* element once the call returns.)
*
* @param o object to be removed from this set, if present
* @return true if the set contained the specified element
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
HashSet的remove(o)也是调用了map的remove(o)方法。
一起再来看下map的remove(o)方法的内容:
/**
* Removes the mapping for the specified key from this map if present.
*
* @param key key whose mapping is to be removed from the map
* @return the previous value associated with key, or
* null if there was no mapping for key.
* (A null return can also indicate that the map
* previously associated null with key.)
*/
public V remove(Object key) {
Entry e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
可以看到如果找到元素,则删除后返回元素的值。因为HashSet使用map的key,存的值都是PRESENT。
所以,map.remove(o)==PRESENT就表示是否找到并删除元素`
方法clear的实现
源代码
/**
* Removes all of the elements from this set.
* The set will be empty after this call returns.
*/
public void clear() {
map.clear();
}
HashSet直接使用map.clear()来移除所有元素~
方法clone的实现
源代码
/**
* Returns a shallow copy of this HashSet instance: the elements
* themselves are not cloned.
*
* @return a shallow copy of this set
*/
public Object clone() {
try {
HashSet newSet = (HashSet) super.clone();
newSet.map = (HashMap) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
返回此HashSet实例的浅表副本:并没有复制这些元素本身。底层实际调用HashMap的clone()方法,获取HashMap的浅表副本,并设置到HashSet中。
序列化&反序列化的实现
源代码
/**
* Save the state of this HashSet instance to a stream (that is,
* serialize it).
*
* @serialData The capacity of the backing HashMap instance
* (int), and its load factor (float) are emitted, followed by
* the size of the set (the number of elements it contains)
* (int), followed by all of its elements (each an Object) in
* no particular order.
*/
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);
}
/**
* Reconstitute the HashSet instance from a stream (that is,
* deserialize it).
*/
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
小结
从上述代码的观察可以看出:
- HashSet底层由HashMap实现
- HashSet的值存放于HashMap的key上
- HashMap的value统一为PRESENT