1、HashSet-底层数据结构

  1. 1HashSet-底层数据结构:
  2. 1-1HashSet这个类实现了Set集合,实际为一个HashMap的实例。
  3. 1-2HashSet,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成。
  4. 1-3HashSet属性字段有:
  5. //底层使用HashMap来保存HashSet中所有元素。
  6. private transient HashMap<E,Object> map;
  7. //定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。
  8. private static final Object PRESENT = new Object();

2、HashSet-构造方法

  1. 1HashSet底层是通过HashMap实现的。
  2. /**
  3. * 默认的无参构造器,构造一个空的HashSet。
  4. *
  5. * 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
  6. */
  7. public HashSet() {
  8. map = new HashMap<E,Object>();
  9. }
  10. /**
  11. * 构造一个包含指定collection中的元素的新set。
  12. *
  13. * 实际底层使用默认的加载因子0.75和足以包含指定
  14. * collection中所有元素的初始容量来创建一个HashMap。
  15. * @param c 其中的元素将存放在此set中的collection。
  16. */
  17. public HashSet(Collection<? extends E> c) {
  18. map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
  19. addAll(c);
  20. }
  21. /**
  22. * 以指定的initialCapacity和loadFactor构造一个空的HashSet。
  23. *
  24. * 实际底层以相应的参数构造一个空的HashMap。
  25. * @param initialCapacity 初始容量。
  26. * @param loadFactor 加载因子。
  27. */
  28. public HashSet(int initialCapacity, float loadFactor) {
  29. map = new HashMap<E,Object>(initialCapacity, loadFactor);
  30. }
  31. /**
  32. * 以指定的initialCapacity构造一个空的HashSet。
  33. *
  34. * 实际底层以相应的参数及加载因子loadFactor为0.75构造一个空的HashMap。
  35. * @param initialCapacity 初始容量。
  36. */
  37. public HashSet(int initialCapacity) {
  38. map = new HashMap<E,Object>(initialCapacity);
  39. }
  40. /**
  41. * 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。
  42. * 此构造函数为包访问权限,不对外公开,实际只是是对LinkedHashSet的支持。
  43. *
  44. * 实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。
  45. * @param initialCapacity 初始容量。
  46. * @param loadFactor 加载因子。
  47. * @param dummy 标记。
  48. */
  49. HashSet(int initialCapacity, float loadFactor, boolean dummy) {
  50. map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
  51. }

3、HashSet-添加元素

  1. 1HashSetadd方法时通过HashMapput方法实现的,不过HashMapkey-value键值对,而HashSet是集合。
  2. 2、源码代码:
  3. private static final Object PRESENT = new Object();
  4. //HashSet添加的元素是存放在HashMap的key位置上,而value取了默认常量PRESENT,是一个空对象
  5. public boolean add(E e) {
  6. return map.put(e, PRESENT)==null;
  7. }

4、HashSet-删除元素

  1. 1HashSetremove方法通过HashMapremove方法来实现。
  2. 2、源码代码:
  3. /**
  4. * 底层实际调用HashMap的remove方法删除指定Entry。
  5. * @param o 如果存在于此set中则需要将其移除的对象。
  6. * @return 如果set包含指定元素,则返回true。
  7. */
  8. public boolean remove(Object o) {
  9. return map.remove(o)==PRESENT;
  10. }
  11. //map的remove方法
  12. public V remove(Object key) {
  13. Node<K,V> e;
  14. //通过hash(key)找到元素在数组中的位置,再调用removeNode方法删除
  15. return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
  16. }
  17. //具体执行删除以及相关方法
  18. final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {
  19. Node<K,V>[] tab; Node<K,V> p; int n, index;
  20. //步骤1.需要先找到key所对应Node的准确位置,首先通过(n - 1) & hash找到数组对应位置上的第一个node
  21. if ((tab = table) != null && (n = tab.length) > 0 &&
  22. (p = tab[index = (n - 1) & hash]) != null) {
  23. Node<K,V> node = null, e; K k; V v;
  24. //1.1 如果这个node刚好key值相同,运气好,找到了
  25. if (p.hash == hash &&
  26. ((k = p.key) == key || (key != null && key.equals(k))))
  27. node = p;
  28. /**
  29. * 1.2 运气不好,在数组中找到的Node虽然hash相同了,但key值不同,很明显不对, 我们需要遍历继续
  30. * 往下找;
  31. */
  32. else if ((e = p.next) != null) {
  33. //1.2.1 如果是TreeNode类型,说明HashMap当前是通过数组+红黑树来实现存储的,遍历红黑树找到对应node
  34. if (p instanceof TreeNode)
  35. node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
  36. else {
  37. //1.2.2 如果是链表,遍历链表找到对应node
  38. do {
  39. if (e.hash == hash &&
  40. ((k = e.key) == key ||
  41. (key != null && key.equals(k)))) {
  42. node = e;
  43. break;
  44. }
  45. p = e;
  46. } while ((e = e.next) != null);
  47. }
  48. }
  49. //通过前面的步骤1找到了对应的Node,现在我们就需要删除它了
  50. if (node != null && (!matchValue || (v = node.value) == value ||
  51. (value != null && value.equals(v)))) {
  52. /**
  53. * 如果是TreeNode类型,删除方法是通过红黑树节点删除实现的,具体可以参考【TreeMap原理实现
  54. * 及常用方法】
  55. */
  56. if (node instanceof TreeNode)
  57. ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
  58. /**
  59. * 如果是链表的情况,当找到的节点就是数组hash位置的第一个元素,那么该元素删除后,直接将数组
  60. * 第一个位置的引用指向链表的下一个即可
  61. */
  62. else if (node == p)
  63. tab[index] = node.next;
  64. /**
  65. * 如果找到的本来就是链表上的节点,也简单,将待删除节点的上一个节点的next指向待删除节点的
  66. * next,隔离开待删除节点即可
  67. */
  68. else
  69. p.next = node.next;
  70. ++modCount;
  71. --size;
  72. //删除后可能存在存储结构的调整,可参考【LinkedHashMap如何保证顺序性】中remove方法
  73. afterNodeRemoval(node);
  74. return node;
  75. }
  76. }
  77. return null;
  78. }

5、HashSet-其他方法

  1. 1HashSet-其他方法:.clear()、.clone()、.contains()、.iterator()、.size()、.isEmpty()等方法。
  2. 2、源码代码:
  3. /**
  4. * 从此set中移除所有元素。此调用返回后,该set将为空。
  5. *
  6. * 底层实际调用HashMap的clear方法清空Entry中所有元素。
  7. */
  8. public void clear() {
  9. map.clear();
  10. }
  11. /**
  12. * 返回此HashSet实例的浅表副本:并没有复制这些元素本身。
  13. *
  14. * 底层实际调用HashMap的clone()方法,获取HashMap的浅表副本,并设置到HashSet中。
  15. */
  16. public Object clone() {
  17. try {
  18. HashSet<E> newSet = (HashSet<E>) super.clone();
  19. newSet.map = (HashMap<E, Object>) map.clone();
  20. return newSet;
  21. } catch (CloneNotSupportedException e) {
  22. throw new InternalError();
  23. }
  24. }
  25. /**
  26. * 如果此set包含指定元素,则返回true。
  27. * 更确切地讲,当且仅当此set包含一个满足(o==null ? e==null : o.equals(e))
  28. * 的e元素时,返回true。
  29. *
  30. * 底层实际调用HashMap的containsKey判断是否包含指定key。
  31. * @param o 在此set中的存在已得到测试的元素。
  32. * @return 如果此set包含指定元素,则返回true。
  33. */
  34. public boolean contains(Object o) {
  35. return map.containsKey(o);
  36. }
  37. /**
  38. * 返回对此set中元素进行迭代的迭代器。返回元素的顺序并不是特定的。
  39. *
  40. * 底层实际调用底层HashMap的keySet来返回所有的key。
  41. * 可见HashSet中的元素,只是存放在了底层HashMap的key上,
  42. * value使用一个static final的Object对象标识。
  43. * @return 对此set中元素进行迭代的Iterator。
  44. */
  45. public Iterator<E> iterator() {
  46. return map.keySet().iterator();
  47. }
  48. /**
  49. * 返回此set中的元素的数量(set的容量)。
  50. *
  51. * 底层实际调用HashMap的size()方法返回Entry的数量,就得到该Set中元素的个数。
  52. * @return 此set中的元素的数量(set的容量)。
  53. */
  54. public int size() {
  55. return map.size();
  56. }
  57. /**
  58. * 如果此set不包含任何元素,则返回true。
  59. *
  60. * 底层实际调用HashMap的isEmpty()判断该HashSet是否为空。
  61. * @return 如果此set不包含任何元素,则返回true。
  62. */
  63. public boolean isEmpty() {
  64. return map.isEmpty();
  65. }