美文网首页
Hash总结

Hash总结

作者: 暑水 | 来源:发表于2019-08-13 10:28 被阅读0次

    HashMap

    原文链接:https://www.cnblogs.com/peizhe123/p/5790252.html

    • hashmap原理:
      HashMap类有一个叫做Entry的内部类。这个Entry类包含了key-value作为实例变量。 每当往hashmap里面存放key-value对的时候,都会为它们实例化一个Entry对象,这个Entry对象就会存储在前面提到的Entry数组table中。Entry具体存在table的那个位置是 根据key的hashcode()方法计算出来的hash值(来决定)。

    • hashCode() 和equals() 方法的重要性
      Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。

    • hash冲突
      散列表要解决的一个问题就是散列值的冲突问题,通常是两种方法:链表法和开放地址法。链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。java.util.HashMap采用的链表法的方式,链表是单向链表。

    HashMap里面没有出现hash冲突时,没有形成单链表时,hashmap查找元素很快,get()方法能够直接定位到元素,但是出现单链表后,单个bucket 里存储的不是一个 Entry,而是一个 Entry 链,系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那系统必须循环到最后才能找到该元素。

    在Java中,HashMap的实现采用了(数组 + 链表 + 红黑树)的复杂结构,数组的一个元素又称作桶。

    在添加元素时,会根据hash值算出元素在数组中的位置,如果该位置没有元素,则直接把元素放置在此处,如果该位置有元素了,则把元素以链表的形式放置在链表的尾部。

    当一个链表的元素个数达到一定的数量(且数组的长度达到一定的长度)后,则把链表转化为红黑树,从而提高效率。

    数组的查询效率为O(1),链表的查询效率是O(k),红黑树的查询效率是O(log k),k为桶中的元素个数,所以当元素数量非常多的时候,转化为红黑树能极大地提高效率。

    当创建 HashMap 时,有一个默认的负载因子(load factor),其默认值为 0.75,这是时间和空间成本上一种折衷:增大负载因子可以减少 Hash 表(就是那个 Entry 数组)所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的的操作(HashMap 的 get() 与 put() 方法都要用到查询);减小负载因子会提高数据查询的性能,但会增加 Hash 表所占用的内存空间。

    Java7扩容时,遍历每个节点,并重新hash获得当前数组的位置并添加到链表中;Java8进一步做了优化,将元素的hash和旧数组的大小(大小为2次幂)做与运算,为0则表示数组位置不变,不为0则表示需要移位,新位置为原先位置+旧数组的小大(新数组大小为旧数组翻倍),并将当前链表拆分为两个链表,一个链表放到原先位置,一个链路放到新位置,效率比Java7高。额外提一点,Java的链表节点数超过8个时,会将链表转化为红黑树,当hash命中很低时,效率比Java7高很多,

    HashMap基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

    值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。

    Map map = Collections.synchronizedMap(new HashMap());
    

    HashMapde 一些关键属性:

    transient Entry[] table;//存储元素的实体数组
     
    transient int size;//存放元素的个数
     
    int threshold; //临界值   当实际大小超过临界值时,会进行扩容threshold = 加载因子*容量
    
     final float loadFactor; //加载因子
     
    transient int modCount;//被修改的次数
    

    相关文章

      网友评论

          本文标题:Hash总结

          本文链接:https://www.haomeiwen.com/subject/hwfvjctx.html