zoukankan      html  css  js  c++  java
  • Java基础知识点介绍-HashMap

    Java为数据结构中的映射定义了一个接口java.util.Map,这个接口主要有四个常用的实现类,分别是HashMap,Hashtable,LinkedHashMap和TreeMap,类继承关系如下图所示:

    下面对个各实现类的特点做一些说明:

    (1)HashMap:它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但是遍历顺序却是不确定的。HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以使用Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。

    (2)HashTable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写Hashtable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以使用ConcurrentHashMap替换。

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

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

    对于上述四种Map类型的类,要求映射中的key是不可变对象,不可变对象是该对象在创建后它的哈希值不会被改变。如果对象的哈希值发生变化,Map对象很可能就定位不到映射的位置了。

    通过上面的比较,我们知道了HashMap是Java的Map家族中一个普通成员,鉴于它可以满足大多数场景的使用条件,所以是使用频度最高的一个。下文主要结合源码,从存储结构、常用方法分析、扩容以及安全性等方面深入讲解HashMap的工作原理。

    内部实现

    搞清楚HashMap,首先需要知道HashMap是什么,即它的存储结构-字段,其次弄明白它能干什么,即它的功能实现-方法。下面我们针对这两个方面详细展开讲解。

    存储结构-字段

    从结构实现来讲,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示:

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

    (1)从源码可知,HashMap类中有一个非常重要的字段,就是Node[] table,即哈希桶数组,明显它是一个Node的数组。我们来看Node[JDK1.8]是何物。

        static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;
            final K key;
            V value;
            Node<K,V> next;
    
            Node(int hash, K key, V value, Node<K,V> next) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = next;
            }
    
            public final K getKey()        { return key; }
            public final V getValue()      { return value; }
            public final String toString() { return key + "=" + value; }
    
            public final int hashCode() {
                return Objects.hashCode(key) ^ Objects.hashCode(value);
            }
    
            public final V setValue(V newValue) {
                V oldValue = value;
                value = newValue;
                return oldValue;
            }
    
            public final boolean equals(Object o) {
                if (o == this)
                    return true;
                if (o instanceof Map.Entry) {
                    Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                    if (Objects.equals(key, e.getKey()) &&
                        Objects.equals(value, e.getValue()))
                        return true;
                }
                return false;
            }
        }

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

    (2)HashMap就是使用哈希表来存储的,哈希表为解决冲突,可以采用开放地址法和链地址法等来解决问题,Java中HashMap采用了链地址法。链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都有一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上。例如执行下面的代码:

    map.put("China","中国");

    系统将调用“China”这个key的hashCode()方法得到其hashCode值(该方法适用于每个Java对象),然后再通过Hash算法的后两步运算(高位运算和取模运算,下文会有介绍)来定位该键值对的存储位置,有事两个key会定位到相同的位置,表示发生了Hash碰撞。当然Hash算法计算结果越分散均匀,Hash碰撞的概率越小,map的存取效率就会越高。

    如果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组很小,即使好的Hash算法也会出现比较多的碰撞,所以就需要在空间成本上和时间成本之间权衡。

  • 相关阅读:
    HDOJ_1010 Tempter of the Bone
    矩阵旋转
    HDU 2544 最短路 http://acm.hdu.edu.cn/showproblem.php?pid=2544
    题目连接:http://acm.zznu.edu.cn/problem.php?id=1329
    队列/优先队列(代码简单模式)
    虚拟方法调用
    Vim中分屏(整理)
    Java Web设计模式之依赖倒换原则
    Java Web 设计模式之开闭原则
    Java 2+2=5
  • 原文地址:https://www.cnblogs.com/LoganChen/p/13950527.html
Copyright © 2011-2022 走看看