• java(HashSet类)


    HashSet介绍:

    1、HashSet实现了Set接口

    2、HashSet实际上是HashMap

    1. public HashSet() {
    2. map = new HashMap<>();
    3. }

    3、可以存放null值,但是只能由一个null

    4、HashSet不保证元素是有序的(存放的数据与取出元素的顺序不一致),取决于hash后,再确定索引的结果

    5、不能有重复元素/对象

    HashSet如何添加元素:

    1.HashSet底层是HashMap

    2.添加一个元素时,先得到hash值-->转成-->索引值

    3.找到存储数据表table,看这个索引位置是否已经存放的有元素

    4.如果没有,则直接加入

    5.如果有,调用equals比较(可以自己设定),如果相同,就放弃添加,如果不相同,则添加到最后

    6.在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认为8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认为64),就会进行树化(红黑树)

    1. //1.执行HashSet()
    2. public HashSet() {
    3. map = new HashMap<>();
    4. }
    5. //2.执行add()方法
    6. public boolean add(E e) {
    7. return map.put(e, PRESENT)==null;
    8. //private static final Object PRESENT = new Object();
    9. }
    10. //3.执行put方法
    11. public V put(K key, V value) {
    12. return putVal(hash(key), key, value, false, true);
    13. }
    14. //执行hash()方法得到一个修改的hash值
    15. static final int hash(Object key) {
    16. int h;
    17. return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    18. }
    19. //4.执行putValue()方法
    20. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
    21. boolean evict) {
    22. Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量
    23. //判断是否初次添加值或者空间大小为零,如果是则开辟大小为16的Node[]
    24. if ((tab = table) == null || (n = tab.length) == 0)
    25. n = (tab = resize()).length;//resize()开辟空间
    26. if ((p = tab[i = (n - 1) & hash]) == null)
    27. //i = (n - 1) & hash 得到对应的索引值
    28. //判断当前索引位置是否为空,为空则直接添加
    29. tab[i] = newNode(hash, key, value, null);
    30. else {
    31. Node<K,V> e; K k;
    32. //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
    33. //并且满足下面两个条件之一的:1、准备加入的key和p指向的Node节点的key是
    34. //同一个对象;2、准备加入的key的内容和p指向的Node节点的key的内容相同
    35. if (p.hash == hash &&
    36. ((k = p.key) == key || (key != null && key.equals(k))))
    37. e = p;
    38. else if (p instanceof TreeNode)
    39. //再判断p是不是一颗红黑树
    40. //如果是,调用putTreeVal()方法进行添加
    41. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    42. else {
    43. //for循环用来比较当前索引后面所接的链表元素
    44. //1、依次和该链表的每一个元素比较后,都不相同,则直接添加在该链表最后
    45. //立即判断该链表是否已经达到8个节点,如果达到则对该链表进行树化(红黑树)
    46. //在转成红黑树时,还要进行判断,如果该table表小于64则先进行扩容
    47. //if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
    48. //resize();当上述条件不满足时,才进行真正的树化
    49. //2、如果链表元素中有相同的,结束循环
    50. for (int binCount = 0; ; ++binCount) {
    51. if ((e = p.next) == null) {
    52. p.next = newNode(hash, key, value, null);
    53. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    54. treeifyBin(tab, hash);
    55. break;
    56. }
    57. if (e.hash == hash &&
    58. ((k = e.key) == key || (key != null && key.equals(k))))
    59. break;
    60. p = e;
    61. }
    62. }
    63. if (e != null) { // existing mapping for key
    64. V oldValue = e.value;
    65. if (!onlyIfAbsent || oldValue == null)
    66. e.value = value;
    67. afterNodeAccess(e);
    68. return oldValue;
    69. }
    70. }
    71. ++modCount;
    72. if (++size > threshold)
    73. resize();//如果数据空间大于12,则再次扩容
    74. afterNodeInsertion(evict);
    75. return null;
    76. }

    扩容机制:

    1、HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75 = 12

    2、如果table数组使用到了临界值12,就会扩容到16*2=32,新的临界值就是32*0.75=24,以此类推

    3、在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认为8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认为64),当两个条件同时满足时就会进行树化(红黑树),否则table表仍采用数组扩容机制

  • 相关阅读:
    SCAU Java 实验7 银行账户存取款业务
    数据结构课程笔记总结1 - 排序算法
    mac上使用svn
    认识线程,初始并发
    计算机毕业设计springboot家居产品的进销存系统dgo68源码+系统+程序+lw文档+部署
    indexDB 本地数据库
    C语言条件运算符——三元表达式例题(素材来自C技能树)
    【Spring】——1、使用@Configuration和@Bean给容器中注册组件
    Chromebook文件夹应用新功能
    springboot+旅游管理系统 毕业设计-附源码261117
  • 原文地址:https://blog.csdn.net/weixin_63954483/article/details/125441338