Java - 提高-源码(6) - HashSet
2017-07-05 22:12
375 查看
HashSet源码解析
源码解析对应JDK1.7
JDK1.7源码下载地址:JDK1.7下载地址
HashSet源码中官方注释是这样描述的:
此类实现了Set接口,由哈希表(实际上是HashMap实例)支持。
对集合的迭代次序不作任何保证; 特别是不能保证订单在一段时间内保持不变。
此类允许null元素。
首先记住结论:
a. HashSet 没有重复元素的集合。
b. HashSet 是无序的。
c. HashSet 元素允许为null。
d. HashSet 非线程安全
HashSet的构造函数
HashSet():
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
HashSet(int initialCapacity):
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor):
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。
HashSet(Collection<? extends E> c):
构造一个包含指定 collection 中的元素的新 set。
HashSet参数
HashSet常用方法
add()方法
这时候e就是key,PRESENT就是value。
当我们第一次插入的时候很顺利,调用addEntry(),插入到Entry中。
那么问题来了,HashSet是怎么通过HashMap来实现元素不重复的呢?
举个栗子:
第二次add相同的数据,依旧调用put("a",PRESENT)
运行到这里:
如果两个key一样,那么将新key的value覆盖旧key的value,也就是说,key始终没有发生变化!!
简单地说当key存在于HashMap的key时,会替换原有的value,但是key保持不变。
所以将一个已经存在的key元素添加到HashSet中,新添加的元素不会保存到HashMap中。
clear()方法
contains()方法
HashSet底层是基于HashMap实现的,如果之前HashMap看明白,看透了,HashSet也是很容易的。
源码解析对应JDK1.7
JDK1.7源码下载地址:JDK1.7下载地址
HashSet源码中官方注释是这样描述的:
This class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. This class permits the null element.大意是:
此类实现了Set接口,由哈希表(实际上是HashMap实例)支持。
对集合的迭代次序不作任何保证; 特别是不能保证订单在一段时间内保持不变。
此类允许null元素。
首先记住结论:
a. HashSet 没有重复元素的集合。
b. HashSet 是无序的。
c. HashSet 元素允许为null。
d. HashSet 非线程安全
HashSet的构造函数
HashSet():
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
HashSet(int initialCapacity):
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor):
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。
HashSet(Collection<? extends E> c):
构造一个包含指定 collection 中的元素的新 set。
HashSet参数
// HashSet的底层容器,没错,是HashMap! private transient HashMap<E, Object> map; // Dummy value to associate with an Object in the backing Map // 定义一个Object对象作为HashMap的value值 private static final Object PRESENT = new Object();
HashSet常用方法
add()方法
public boolean add(E e) { return map.put(e, PRESENT) == null; }add()方法底层调用的是HashMap的put方法;
这时候e就是key,PRESENT就是value。
public V put(K key, V value) { // 如果key为空,将null存放在table[0]第一个位置,这就是HashMap允许存null的原因 if (key == null) return putForNullKey(value); // 计算key的hash值 int hash = hash(key); // 根据hash码和数组长度,计算table数组下标 int i = indexFor(hash, table.length); // 从i处开始迭代entry链表,找到key保存的位置 for (Entry<K, V> e = table[i]; e != null; e = e.next) { Object k; // 判断该链条上是否有hash值相同的(key相同) // 若存在key相同,直接覆盖value,返回旧的value if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value;// 取出旧值 e.value = value;// 赋新值 e.recordAccess(this); return oldValue;// 返回旧值 } } // 修改次数+1 modCount++; // i处没有entry链表(该位置为空),将key,value添加至i处 addEntry(hash, key, value, i); return null; }这个源码很熟悉吧..HashMap中的put方法。
当我们第一次插入的时候很顺利,调用addEntry(),插入到Entry中。
那么问题来了,HashSet是怎么通过HashMap来实现元素不重复的呢?
举个栗子:
HashSet<String> set = new HashSet<String>(); set.add("a"); set.add("a");第一次add时候,调用put("a",PRESENT),之后没问题,看源码就行。
第二次add相同的数据,依旧调用put("a",PRESENT)
运行到这里:
// 从i处开始迭代entry链表,找到key保存的位置 for (Entry<K, V> e = table[i]; e != null; e = e.next) { Object k; // 判断该链条上是否有hash值相同的(key相同) // 若存在key相同,直接覆盖value,返回旧的value if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value;// 取出旧值 e.value = value;// 赋新值 e.recordAccess(this); return oldValue;// 返回旧值 } }你看啊,因为两次的key是一样的,计算出来的下标也是一样的,这时候就开始比较key了
如果两个key一样,那么将新key的value覆盖旧key的value,也就是说,key始终没有发生变化!!
简单地说当key存在于HashMap的key时,会替换原有的value,但是key保持不变。
所以将一个已经存在的key元素添加到HashSet中,新添加的元素不会保存到HashMap中。
clear()方法
/** * Removes all of the elements from this set. The set will be empty after * this call returns.<br> * 从该集合中删除所有元素。 此通话返回后,该设置将为空。 */ public void clear() { map.clear(); }调用了底层map的clear方法;
public void clear() { modCount++; Entry[] tab = table; for (int i = 0; i < tab.length; i++) tab[i] = null; size = 0; }将map清空...
contains()方法
public boolean contains(Object o) { return map.containsKey(o); }底层调用了map的containsKey方法。
public boolean containsKey(Object key) { return getEntry(key) != null; }跟下去看getEntry()方法
final Entry<K, V> getEntry(Object key) { // 计算key的hash值 int hash = (key == null) ? 0 : hash(key); // 根据hash值,算出下标位置,从table数组中取出Entry for (Entry<K, V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; // 查找的key与entry中的key相同,则返回对应的value if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }注释写的比较清楚了不解释了...
HashSet底层是基于HashMap实现的,如果之前HashMap看明白,看透了,HashSet也是很容易的。
相关文章推荐
- 【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析
- Thinking in Java之Set接口、HashSet源码学习
- Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例
- Java集合(10)--HashSet源码分析
- java源码分析之HashSet及LinkedHashSet
- Java记录 -70- HashSet源码剖析
- 使用 AppFuse 的七个理由--学习 Java 开放源码工具 —— 并使用这些工具提高生产效率
- java.util.hashSet的源码剖析
- Java集合之HashSet源码分析
- 【转】Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例--不错
- java提高篇(二四)-----HashSet
- Java Collections Framework之HashSet及LinkedHashSet源码分析(基于JDK1.6)
- 【Java源码】HashSet、LinkedHashSet
- java提高篇(二四)-----HashSet
- Java集合系列之HashSet源码分析
- Thinking in Java之Set接口、HashSet源码学习
- java提高篇(二四)-----HashSet
- Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例
- java-HashMap和HashSet源码分析
- java提高篇(二四)-----HashSet