• HashMap—基本结构


    前言


           数组的特点是:寻址容易,插入和删除困难,数组利用下标定位,时间复杂度为O(1),插入或删除元素的时间复杂度O(n)。

           链表的特点是:寻址困难,插入和删除容易,链表定位元素时间复杂度O(n),插入或删除元素的时间复杂度O(1)。

           那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?

           HashMap是结合两者优势,这是一种折中的方案。(在java8中引入了红黑树,是对性能的更进一步优化)

    HashMap内部结构


    HashMap实际上是一个数组,数组里面的每个元素都是一个链表。每个元素在通过put方法放入HashMap中的时候,要按照如下步骤进行:

    1.根据该元素自身提供的hashcode计算出散列值,该散列值就是数组的下标

    2.将新元素放入该数组位置的链表中

    数组定义:

    1. /**
    2.      * The table, resized as necessary. Length MUST Always be a power of two.
    3.      */
    4. transient Entry[] table; 

          这是一个数组,transient关键字告诉我们它不会参与序列化。既然是一个数组,总有数目上限,也就意味着如果存入HashMap的元素太多,导致数组大小不能够存放所有的链表的时候,数组大小必须要能够调整。

    Entry是什么类型?

     1 static class Entry<K,V> implements Map.Entry<K,V> {
     2         final K key;
     3         V value;
     4         Entry<K,V> next;
     5         final int hash;
     6 
     7         /**
     8          * Creates new entry.
     9          */
    10         Entry(int h, K k, V v, Entry<K,V> n) {
    11             value = v;
    12             next = n;
    13             key = k;
    14             hash = h;
    15         }
    16         ....
    17         public final boolean equals(Object o) {
    18             if (!(o instanceof Map.Entry))
    19                 return false;
    20             Map.Entry e = (Map.Entry)o;
    21             Object k1 = getKey();
    22             Object k2 = e.getKey();
    23             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
    24                 Object v1 = getValue();
    25                 Object v2 = e.getValue();
    26                 if (v1 == v2 || (v1 != null && v1.equals(v2)))
    27                     return true;
    28             }
    29             return false;
    30         }
    31 
    32         public final int hashCode() {
    33             return (key==null   ? 0 : key.hashCode()) ^
    34                    (value==null ? 0 : value.hashCode());
    35         }
    36         ....
    Entry部分源码

    这是一个HashMap类的内部静态类。实现了Map.Entry接口。接受两个模板参数K和V。key和hash一旦在构造函数中被初始化,就不可改变,并且由于有next的存在,Entry可以构成一个单向链表。

    HashMap 包含如下几个构造器:

    1. HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。

    2. ashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。

    3. HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。

    HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和负载因子loadFactor。

    负载因子loadFactor衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

    HashMap的实现中,通过threshold字段来判断HashMap的最大容量:

    threshold = (int)(capacity * loadFactor);

    结合负载因子的定义公式可知,threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实的负载因子。默认的的负际载因子0.75是对空间和时间效率的一个平衡选择。当容量超出此最大容量时, resize后的HashMap容量是原来容量的两倍.

     

    参考

    http://www.codeceo.com/article/java-hashmap-learn.html

    http://blog.csdn.net/csfreebird/article/details/7347026

    http://www.cnblogs.com/xwdreamer/archive/2012/05/14/2499339.html

  • 相关阅读:
    Jmeter_Beanshell_使用Java处理JSON块
    BeanShell Processor_使用Java处理脚本
    MySQL_explain关键字分析查询语句
    LoadRunner11_录制脚本时的浏览器版本
    Jmeter_实现Excel文件导出到本地
    Jmeter_录制HTTPS
    性能测试常用sql语句
    LoadRunner11_MySQL数据库脚本
    LoadRunner11_录制Oracle数据库脚本
    实现liunx之间无密码访问——ssh密匙
  • 原文地址:https://www.cnblogs.com/sasuke-y/p/5664774.html
Copyright © 2020-2023  润新知