一、基本图示
二、基本介绍
结构
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
- HashSet 继承了 AbstractSet 抽象类,AbstractSet 接口继承了 AbstractCollection 抽象类
- HashSet 实现了 Set 接口
- HashSet 实现了 Coneable 类,覆盖了 clone 方法,可以被克隆
- HashSet 实现了 Serializable 接口,即能被序列化和反序列化
特性
- HashSet 底层依然是由 HashMap 实现的
- 元素的顺序是无序的
- HashSet 的操作是线程不安全的
- HashSet 中可以存放 null 的(因为 HashMap 允许存放 key 为 null 的键值对),不允许存放重复的值,包括重复的 null 值
三、基本参数
HashSet 的底层是由 HashMap 实现的,HashSet 将值存放在 HashMap 的 key 中,value 则是同一个对象
private transient HashMap<E,Object> map;
这里的 PRESENT 其实是同一个对象,存放在 HashMap 的 value 中,所有的 key 都对应这同一个 value,即 PRESENT 对象
private static final Object PRESENT = new Object();
四、初始化方法
无参构造函数,实例化一个空的HashMap,初始容量为 16,加载因子为 0.75
public HashSet() {
map = new HashMap<>();
}
如果传入初始容量,那么底层的 HashMap 的初始容量就是传入的值,加载因子还是 0.75
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
如果既传入初始容量,又传入负载因子,则底层的 HashMap 使用的就是传入的初始容量和负载因子
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
传入一个 Collection 集合对象
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
五、添加元素
在 HashSet 中,添加元素在底层实际是对 HashMap 进行操作,即调用了 HashMap 的 put 方法。key 是添加的元素,value 则是同一个 PRESENT 对象
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashSet 的 add 方法里面调用 HashMap 的 put 方法,对于放入的值有如下的规定:
- 传入的 null 永远只会放在数组的第 0 位,且只能放1个 null,因为HashMap 中的 value 永远是同一个对象,因此后面传入的 null 会将前面传入的 null 覆盖
- 先根据传入值的 hash 值计算在数组中的位置,然后遍历数组中在这个位置上的单链表,如果有和该值的 hash 值和 key 值一样,或者 key 的地址一样的,则在单链表中使用新值将旧的值覆盖
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
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;
}
六、删除元素
HashSet 的 remove 方法,底层调用的是 HashMap 的 remove 方法
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
该方法根据 key 删除 Entry 键值对
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
七、遍历HashSet
在对 HashSet 进行遍历的时候,分两步来执行:
扫描二维码关注公众号,回复:
5647640 查看本文章
- 调用 HashMap 的 keySet() 方法获取键的集合
- 调用 HashMap 的内部类 KeySet 的 iterator 方法对键的集合进行遍历
public Iterator<E> iterator() {
return map.keySet().iterator();
}
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
...
}
八、其他方法
获取集合长度
public int size() {
return map.size();
}
判断集合是否为空
public boolean isEmpty() {
return map.isEmpty();
}
判断集合是否包含某一对象
public boolean contains(Object o) {
return map.containsKey(o);
}