JAVA中的哈希表HashMap集合

    技术2025-12-03  18

    哈希表集合HashMap:

    HashMap的底层代码: public class HashMap{ //HashMap底层实际上就是一个一维数组 Node<K, V>[] table; //静态内部类HashMap.Node static class Node<K, V> implements Map.Entry<K,V> { final int hash; //哈希值是key的hashCode()方法的执行结果, //hash值通过哈希函数/算法,可以转换成数组的下标 final K key; //存储到Map集合中的key V value; //存储到Map集合中的value Node<K, V> next; //下一个节点的内存地址 } } map.put(k,v)实现原理: 第一步:先将k,v封装到Node对象中 第二步:底层会调用k的hashCode()方法得出hash值,然后通过哈希函数/哈希算法, 将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置, 如果下标对英国的位置上有链表,此时会拿k和链表上每一个节点中的k进行equals, 如果所有的equals方法都返回false,那么这个新节点将被添加到链表的末尾, 如果其中有一个equals方法返回了true,那么这个节点的value值将被覆盖。V v = map.get(k)实现原理: 先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下表, 通过数组下标快速定位到某个位置,如果这个位置上没有链表,则返回null, 如果这个位置上有单向链表,则会将k与单向链表上每个节点的k进行equals, 如果所有equals都返回false,那么get()方法返回null, 如果有一个节点的equals返回true,那么此时这个节点的value将被返回由put和get方法可知,hashCode()和equals()方法都要重写。同一个单向链表上所有节点的int hash相同,因为他们的数组下标是一样的哈希表的查询方式就像是查字典: 比如我查一个“中”字,有两种方式, 第一种——是从头一页一页查,直到找到365页上的“中”字为止,这是单向链表查询方式 第二种——是在目录中找到“zhong”,指向了360页(这是哈希表中的数组的查询方式), 那么我们就是从360页开始找起,直到找到365页的“中”字, (这就是哈希表中的的单向链表查询方式) 由上述例子可知,哈希表查询效率比数组低,增删效率比单向链表低。哈希表HashMap使用不当时无法发挥性能: 假设所有的hashCode()方法返回一个固定值,那么哈希表就变成了一个纯单向链表, 这种情况成为“散列分布不均匀” 假设所有的hashCode()方法返回的值都不同,那么哈希表就变成了一个一维数组, 也是“散列分布不均匀” 散列分布均匀需要在重写hashCode方式时有一定的技巧HashMap集合的默认初始化容量是16,默认加载因子是0.75 默认加载因子是当HashMap集合底层数组的容量达到75%时,数组开始扩容 每次扩容为原容量的2倍 重点:HashMap集合初始化容量必须是2的幂,这是因为达到散列分布均匀, 为了提高HashMap集合的存取效率所必须的在JDK8之后,如果哈希表的单向链表中元素大于或等于8个或数组长度大于或等于64,哈希表会转变成红黑树数据结构, 当红黑树上的节点数量小于6的时候,会重新把红黑树转变成单向链表数据结构, 这种方式是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率HashMap集合key部分允许null么? 允许 但要注意:HashMap集合的key null值只能有一个 public class HashMapTest03 { public static void main(String[] args) { Map map = new HashMap(); map.put(null, null); System.out.println(map.size()); //1 System.out.println(map.get(null)); //null map.put(null, "abc"); System.out.println(map.size()); //1 System.out.println(map.get(null)); //abc } }
    Processed: 0.057, SQL: 9