Set集合之HashSet
定义
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
HashSet
继承了AbstractSet
实现了Set接口,支持Set接口中定义的操作
实现了Cloneable接口,支持对象克隆
实现了Serializable接口,支持对象序列化,反序列化
底层数据结构
HashSet的集合元素都是存储在map的key中
private transient HashMap<E,Object> map;
这里map是HashMap集合。所以我们知道HashSet底层依赖于HashMap的实现。所以HashSet的数据结构也就是HashMap的数据结构。
所以HashSet集合的底层数据结构可以说是哈希表,更加具体是【数组+单向链表+红黑树】
HashSet集合特点
HashSet集合元素 无序,不可重复。
HashSet集合元素相当于HashMap集合的key列数据。
HashSet集合元素的无序性,是指元素插入顺序和取出顺序不一致。但是HashSet存储元素是基于哈希表存入的,所以元素的存储位置是哈希函数决定的。即(n-1)& hash。
HashSet集合元素可以为null,因为HashMap的key可以为null。
HashSet的集合元素不可重复,其实是指HashSet集合元素不会重复,因为HashMap的key列元素不会重复,当在HashMap中插入重复key的键值对时,只会覆盖老的value值,而保持其key唯一。
HashSet集合元素的哈希冲突判断
由于HashSet集合元素就是HashMap的key列。
所以HashSet集合元素的存储也要可能发生哈希冲突。
当ele和oldEle通过哈希函数(n-1)& hash 计算出来的索引位置相同时,
hash(ele) == hash(oldEle) && (ele == oldEle || (ele != null && ele.equals(oldEle)))
且当上面判断为true时,说明ele和oldEle相同,未发生哈希冲突。
若上面判断为false时,则说明ele和oldEle不同,则发生哈希冲突。
发生哈希冲突时,冲突的HashSet集合元素会以单向链表或者红黑树挂载在桶位置下面。
成员变量
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
serialVersionUID : 序列化ID,与HashSet存储数据能力实现无关
map : HashSet集合元素实际存储的地方,即HashSet集合元素其实都是存储在HashMap的key列中。
PRESENT : 一个Object类型对象,由于HashMap集合是双列集合,其中key列存储的是HashSet的集合元素,而所有的key对应的value列数据都是PRESENT;
构造器
public HashSet()
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
HashSet无参构造器,内部完成map初始化,即将map初始化为new HashMap();
最终map底层哈希表长度为16,负载因子为0.75
public HashSet(Collection<? extends E> c)
/**
* Constructs a new set containing the elements in the specified
* collection. The <tt>HashMap</tt> 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);
}
HashSet指定初始集合元素的构造器,内部完成map初始化
其中map初始化的容量为 c.size()/0.75 和 16的最大值
之后使用addAll将集合c中元素添加进当前HashSet集合中
public HashSet(int initialCapacity)
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and default load factor (0.75).
*
* @param initialCapacity the initial capacity of the hash table
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
指定初始化容量的HashSet构造器
内部完成map初始化,初始化容量为指定的初始化容量,但是map底层哈希表实际容量为tableSizeFor(initialCapacity)
public HashSet(int initialCapacity, float loadFactor)
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> 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);
}
指定初始化容量和负载因子的HashSet构造器
内部完成map的初始化,map底层哈希表的初始化容量为tableSizeFor(initialCapacity),负载因子为loadFactor
HashSet(int initialCapacity, float loadFactor, boolean dummy)
/**
* Constructs a new, empty linked hash set. (This package private
* constructor is only used by LinkedHashSet.) The backing
* HashMap instance is a LinkedHashMap with 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
* @param dummy ignored (distinguishes this
* constructor from other int, float constructor.)
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
该构造器为默认访问权限,即同包访问,即我们日常使用时,无法使用该构造器。
该构造器主要是给HashSet子类LinkedHashSet使用。
成员方法
public boolean add(E e)
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
可以看出HashSet的add方法内部调用HashMap的put方法
HashMap的put方法具有新增和修改功能。其中新增是对于key-valule(返回null),修改是对于value(返回老的value)
而对于HashSet而言,只关注map的key列,value数据永远时PRESENT。所以map的put修改功能对key(HashSet的集合元素)无效果,或者说put修改功能在HashSet体现为永远返回PRESENT。
(注意这里就是为什么将HashSet底层HashMap的value设置为PRESENT,而不是null的原因。如果图方便,将HashSet底层的map的value设置为null,则put重复key时,会导致put方法返回的被覆盖的value值总是null,那么就无法判断HashSet的add方法是否添加key成功了)
所以HashSet新增重复元素时,底层map的key列无变化。此时add方法返回false。
新增不重复元素时,底层map的key会新增元素,此时add方法返回true。
public boolean remove(Object o)
/**
* Removes the specified element from this set if it is present.
* More formally, removes an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>,
* if this set contains such an element. Returns <tt>true</tt> 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 <tt>true</tt> if the set contained the specified element
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
HashSet的remove方法依赖于底层HashMap的remove方法
我们知道HashMap的remove方法,删除存在的key的键值对时,会将对应的value作为方法返回值,删除不存在的key的键值对时,会直接返回null。
而HashSet底层的HashMap的value永远是PRESENT。所以如果删除的元素(key)存在,则会返回PRESENT,否则返回null。
这就可以帮助HashSet的remove方法判断是否删除成功了。
public void clear()
/**
* Removes all of the elements from this set.
* The set will be empty after this call returns.
*/
public void clear() {
map.clear();
}
HashSet的clear方法依赖于底层map的clear方法
public boolean contains(Object o)
/**
* Returns <tt>true</tt> if this set contains the specified element.
* More formally, returns <tt>true</tt> if and only if this set
* contains an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this set is to be tested
* @return <tt>true</tt> if this set contains the specified element
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
HashSet的contains方法依赖于底层map的containsKey方法
public int 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();
}
HashSet的size方法依赖于底层map的size方法
public boolean isEmpty()
/**
* Returns <tt>true</tt> if this set contains no elements.
*
* @return <tt>true</tt> if this set contains no elements
*/
public boolean isEmpty() {
return map.isEmpty();
}
HashSet的isEmpty方法依赖于底层map的isEmpty方法
public Object clone()
/**
* Returns a shallow copy of this <tt>HashSet</tt> instance: the elements
* themselves are not cloned.
*
* @return a shallow copy of this set
*/
@SuppressWarnings("unchecked")
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(e);
}
}
可以看出HashSet的clone()方法返回的是浅克隆对象,因为没有针对集合元素对象的克隆
迭代器
public Iterator<E> 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<E> iterator() {
return map.keySet().iterator();
}
HashSet的迭代器对象通过iterator()方法返回。
而该方法内部依赖于底层map.keySet().iterator()。map.keySet().iterator()返回的是KeyIterator迭代器对象,该类重写next方法。
而KeyIterator类继承了HashIterator,HashIteator提供了hasNext(),remove()方法
相关文章
- set集合的特点
- 使用循环遍历字典和set集合
- set max_containsvalue方法
- 不安全的集合类Set
- Java集合Set接口详解——含源码分析
- set 方法是坏味道?
- Set集合
- 【Redis】Redis 集合 Set 操作 ( Set 集合数据 | 查询操作 | 查询所有值 | 随机获取值 | 获取交集并集差集 | 增操作 | 删操作 | 修改操作 )
- ORA-19626: backup set type is string – can not be processed by this conversation ORACLE 报错 故障修复 远程处理
- ORA-27354: attribute string cannot be set for lightweight jobs ORACLE 报错 故障修复 远程处理
- ORA-38488: attribute set already assigned to the column storing expressions ORACLE 报错 故障修复 远程处理
- ORA-38499: expression set already configured for stored/indexed attributes ORACLE 报错 故障修复 远程处理
- ORA-39914: Index string.string in tablespace string points to subpartition string of table string.string in tablespace string outside of transportable set. ORACLE 报错 故障修复 远程处理
- ORA-00108: failed to set up dispatcher to accept connection asynchronously ORACLE 报错 故障修复 远程处理
- MySQL Error number: MY-010172; Symbol: ER_CANT_SET_DATADIR; SQLSTATE: HY000 报错 故障修复 远程处理
- MySQL Error number: MY-013989; Symbol: ER_CHECK_TABLE_INSTANT_VERSION_BIT_SET; SQLSTATE: HY000 报错 故障修复 远程处理
- Redis清空Set的一秒操作(redis清空set)
- MySQL中使用SET字段类型的方法及注意事项(mysql中使用set)
- 深入了解MySQL中的SET集合使用方法(mysql中set集合)
- MySQL中SET语句的使用及注意事项(mysql中set语句)
- MySQL中SET的常见用法及示例解析(mysql中set 用法)
- 删除Redis中无用的Set(删除set redis)
- 深入浅出Redis集群与SET集合(redis集群set集合)
- 限制集合Redis解决方案(redis限制set)
- Redis实现Set对象的过滤功能(redis过滤set对象)
- Oracle SET指令实现变量显示(oracle set显示)
- 利用Oracle Set 快速进行判断(oracle set判断)