zoukankan      html  css  js  c++  java
  • HashMap的底层实现原理

         HashMap 是一个散列表,它存储的内容是键值对(key-value)映射,它是通过拉链法解决哈希冲突的。。继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外HashMap中的映射不是有序的。

         HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”,默认初始容量16,初始容量必须是2的幂。容量 是哈希表中桶的数量,初始容量 只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作

    HashMap有四个构造函数:默认构造函数,使用默认的容量大小及加载因子,然后计算阈值,创建entry数组用来保存数据,进行初始化,HashMap();指定“容量大小”的构造函数,调用的是HashMap(int capacity, float loadFactor),只不过,加载因子使用的是默认加载因子,HashMap(int capacity);指定“容量大小”和“加载因子”的构造函数,HashMap(int capacity, float loadFactor),先判断传入的参数是否为正数,当初始容量大于最大初始容量时(最大初始容量是2的30次方),使用最大初始容量,传入的初始容量值不是2的幂时,会寻找大于该值的最小的2的幂,后面与默认构造函数相同;包含“子Map”的构造函数,HashMap(Map<? extends K, ? extends V> map),使用子map的大小与默认加载因子求出初始容量,并与默认初始容量相比较,取最大值作为初始容量,调用指定“容量大小”和“加载因子”的构造函数,加载因子取默认加载因子,最后将子map中的全部元素逐个添加到hashmap中。

    Hashmap有几个主要的对外接口,void clear(), clone(),containsKey(Object key),containsValue(Object value),entrySet(),get(Object key),isEmpty(),keySet(),put(K key, V value),putAll(Map<? extends K, ? extends V> map),remove(Object key),size(),values();clear() 的作用是清空HashMap。它是通过将所有的元素设为null来实现的。

    containsKey() 的作用是判断HashMap是否包含key。containsKey() 首先通过getEntry(key)获取key对应的Entry,然后判断该Entry是否为null。getEntry() 的作用就是返回键为key”的键值对;

    containsValue() 的作用是判断HashMap是否包含值为value”的元素,如果value为null,执行containsNullValue(),containsNullValue() 的作用判断HashMap中是否包含值为null”的元素,containsNullValue()分为两步进行处理:第一,若“value为null”,则调用containsNullValue()。第二,若“value不为null”,则查找HashMap中是否有值为value的节点。     entrySet()的作用是返回“HashMap中所有Entry的集合,它是一个集合,HashMap是通过拉链法实现的散列表。表现在HashMap包括许多的Entry,而每一个Entry本质上又是一个单向链表,那么HashMap遍历key-value键值对的时候,可通过entrySet()遍历。

    get() 的作用是获取key对应的value,先获取key的hash值,然后在“该hash值对应的链表”上查找“键值等于key”的元素。

    put() 的作用是对外提供接口,让HashMap对象可以通过put()“key-value”添加到HashMap,若要添加到HashMap中的键值对对应的key已经存在HashMap中,则找到该键值对;然后新的value取代旧的value,并退出!若要添加到HashMap中的键值对对应的key不在HashMap中,则将其添加到该哈希值对应的链表中,并调用addEntry()。说到addEntry(),就不得不说另一个函数createEntry(),它们的作用都是将key、value添加到HashMap中,但addEntry()一般用在 新增Entry可能导致“HashMap的实际容量超过阈值的情况下。例如,我们新建一个HashMap,然后不断通过put()向HashMap中添加元素;put()是通过addEntry()新增Entry的。在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”;因此,需要调用addEntry()。createEntry() 一般用在 新增Entry不会导致“HashMap的实际容量超过阈值的情况下。例如,我们调用HashMap“带有Map”的构造函数,它绘将Map的全部元素添加到HashMap中;但在添加之前,我们已经计算好“HashMap的容量和阈值”。也就是,可以确定“即使将Map中的全部元素添加到HashMap中,都不会超过HashMap的阈值”。此时,调用createEntry()即可。

    putAll() 的作用是"m"的全部元素都添加到HashMap,计算容量是否足够,若“当前实际容量 < 需要的容量”,则将容量x2。通过迭代器,将“m”中的元素逐个添加到HashMap中。

    remove() 的作用是删除键为key”元素,先获取哈希值。若keynull,则哈希值为0;否则调用hash()进行计算,然后删除链表中“键为key”的元素,本质是“删除单向链表中的节点”。

  • 相关阅读:
    Eclipse集成Maven的Web工程demo(独立及Maven集成tomcat)
    Spring Boot的常见配置项解析
    SpringBoot入门demo
    简单句障碍的解决
    阅读理解(2000年统考)
    Java Web项目实战第1篇之环境搭建
    [STM32F10x] 利用定时器测量脉冲宽度
    [STM32F10x] 利用定时器测量频率
    STM32 输入捕获的脉冲宽度及频率计算
    RT-Thread—STM32—在线升级(Ymodem_OTA、HTTP_OTA)
  • 原文地址:https://www.cnblogs.com/xiaoxli/p/9548341.html
Copyright © 2011-2022 走看看