• HashMap学习


    简介

    Java为数据结构中的映射定义了一个接口java.util.Map

    1、HashMap:它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度。HashMap最多只允许一条记录的键为null,允许多条记录的值为null。非线程安全。如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap

    2、Hashtable:Hashtable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类。线程安全。并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。

    3、LinkedHashMap:LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。

    4、TreeMap:TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。
    在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。

    内部实现

    (1) 存储结构-字段
    (2) 功能实现-方法

    存储结构-字段

    HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的。

    这里需要讲明白两个问题:数据底层具体存储的是什么?这样的存储方式有什么优点呢?

    HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组

     1 static class Node<K,V> implements Map.Entry<K,V> {
     2    final int hash;    //用来定位数组索引位置   
     3    final K key;
     4    V value;
     5    Node<K,V> next;   //链表的下一个node   
     6    Node(int hash, K key, V value, Node<K,V> next) { ... }
     7    public final K getKey(){ ... }
     8    public final V getValue() { ... }
     9    public final String toString() { ... }
    10    public final int hashCode() { ... }
    11    public final V setValue(V newValue) { ... }
    12    public final boolean equals(Object o) { ... }
    13 }

    Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)。

    默认容量和加载因子 

     HashMap有两个参数影响其性能:初始容量加载因子默认初始容量是16,加载因子是0.75。容量是哈希表中桶(Entry数组)的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。

    源码

    1 static final int DEFAULT_INITIAL_CAPACITY = 16;
    2 static final float DEFAULT_LOAD_FACTOR = 0.75f;
    3 int threshold;
    4     public HashMap() {
    5       this.loadFactor = DEFAULT_LOAD_FACTOR; 
    6       threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
    7       table = new Entry[DEFAULT_INITIAL_CAPACITY];
    8       init();
    9  }

    默认的容量是16,而 threshold是16*0.75 = 12;相对准确的估算数据量,将极大的影响HashMap的性能,因为resize是一个重新分配的过程,耗时应该是里面最大的。加载因子较小,会有更多的空间空闲,我不知道这个0.75是不是一个折中方案。也许0.9也是一个不错的选择,特别是那些数据量虽然很大,但不是经常变化的地方,比如公司人员,城市列表等相对比较固定的数据。

    小结

        (1) 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。

        (2) 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。

        (3) HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。

        (4) JDK1.8引入红黑树大程度优化了HashMap的性能。

  • 相关阅读:
    [Nginx] 解决跨域been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
    [GO项目]开源免费在线客服系统-真正免费开源-GOFLY0.3.2发布-极简强大Go语言开发网页客服
    [PHP] php使用curl_multi_exec并行同时执行http请求
    [PHP] PHP redis滑动窗口频率限制
    [PHP] xml转为数组函数
    [javascript] 全国省市县JSON/XML数据(包含台湾和海外国家一级)
    [MySQL] 注意MySQL5.1不支持utf8mb4 Error 1115: Unknown character set: 'utf8mb4'
    [Go] linux下安装go1.16
    [GO项目]开源免费在线客服系统-真正免费开源-GOFLY0.2.9发布-极简强大Go语言开发网页客服
    [PHP]PHP不支持方法重载和只支持方法覆盖
  • 原文地址:https://www.cnblogs.com/PreachChen/p/8901813.html
Copyright © 2020-2023  润新知