HashMap 深度理解

数据结构中用数组和链表实现数据的存储

    数组: 数组存储区间是连续的, 占用内存, 寻址很快. 根据下标index寻找元素. 修改容易. 插入和删除时, 需要把后面的元素一次复制到前一个或后一个位置, 插入和删除困难.

    链表: 链表的存储空间可以不是连续的, 占用内存比较宽松. 寻址比数组慢, 需要从开始或结尾依次遍历元素, 如果不是根据元素中存储的下一个元素的指针继续查询下一个元素. 修改,插入和删除速度快

    hashtable 综合了数组和链表的优势, 寻址容易, 插入和删除也容易. 但是某些情况下占用较多的内存(TODO)

    hashtable 的拉链法: 

        如何实现:  eg. 在一个长度为16的数组中, 每一个数组元素(也称之为桶)都存储一个由entry组成的链表. 每一个元素所在链表由其hash值 对数组length 16 取余决定, 即 index = hash % len. 

        HashMap是由一个线性的数组实现的, 首先, 其内部实现一个静态内部类 Entry, 其主要属性有key, value, next. HashMap 就是一个entry数组, 即entrys[].

        有时, key值的hash值相同或对entrys.length取余的余数相同, 这时, 原有的entry的next指向下一个元素, 新加入的entry的next指向原有的entry, entrys[]中对应的元素改成新的entry, 这样就形成了一个链表, 链表头是最新的元素, 链表尾是最早的元素, next指向entrys[]的下一个数组元素. 该实现假定哈希函数将元素均匀的分布在每个元素(桶)中. 数组的每个元素(桶)都指向一个entry链表.

        HashMap中key值null的存取:

            null会存储在entry数组的第一个元素里. 具体位置需要遍历entrys[0]这个链表.

HashMap的一些优化:

        为了防止链表过长, 在hashMap中设置一个加载因子和默认容量, 当hashMap的元素数量大于容量 * 加载因子时, hashMap会自动扩容, 同时进行 rehash. 元素的长度(桶的数量)大约为之前的2倍.

        加载因子过高会导致链表变长, 增加查询成本. 过低会频繁扩容, 对性能造成影响.

        注意: 如果初始容量大于最大条目数除以加载因子, 则不会发生 rehash 操作. 此时桶的数量固定, 链表长度会不断增加, 查询时间变长.

实现Map接口的集合还有enumMap, TreeMap

    treeMap是基于红黑树的 NavigableMap 实现. 根据key值的自然顺序排序. 或根据构造方法中提供的Comparator(比较器)排序. 不允许重复的key值. TreeMap线程不安全的. 可以使用Collections包中的synchronizedSortedMap方法构造线程安全的TreeMap集合.

    enumMap是与枚举类型key一起使用的专用Map实现. Enum枚举类是在java5中引入的. 枚举Map中所有的key都必须来自某个Enum类. 该枚举类型在创建映射时显式或隐式地指定. EnumMap在内部表示为数组, 因此非常紧凑高效. EnumMap是不同步的. 使用Collections.synchronizedMap(java.util.Map)方法包装成线程安全的EnumMap集合. 基本所有操作 (put/ get) 都在固定时间内完成, 一般比hashMap更快. 


猜你喜欢

转载自blog.csdn.net/secret_breathe/article/details/80879581
今日推荐