Java集合:HashMap

时间:2019-09-23
本文章向大家介绍Java集合:HashMap,主要包括Java集合:HashMap使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、集合概览

1. 集合的数据结构有Map/List/Set

2. 集合继承关系

二、HashMap定义:

1. Hash表(key-value),根据key(hash code)找到对应的value;会有hash冲突

2. 是基于Hash表的Map实现,Map即key-value的接口

public class HashMap<K,V>
    extends AbstractMap<K,V>  // 继承AbstractMap抽象类,这个抽象类提供了Map接口的最主要功能的实现
    implements Map<K,V>, Cloneable, Serializable   // 实现Map接口,这个接口定义了键映射到值的规则

三、 装载因子:

1. 装载因子表示hash表中元素的填满程度,装载因子越大则空间利用率高,冲突机会也大,查找耗时也越大

2. HashMap容量:

a. HashMap默认最大容量16,默认装载因子0.75,即阈值12

b. 容量即数据结构中数组的大小

c. 一旦大于等于阈值,2倍扩大空间,最大容量2^30

d. 扩大空间是指创建一个新的2倍大的数组,并把原来的对象放入新的数组中,这个过程叫rehashing

3. 扩容会损耗性能,如果能预估HashMap中元素个数,预设个数能提高性能

HashMap():默认初始容量(16)和加载因子(0.75)

HashMap(int initialCapacity):可以自定义初始容量,预估个数能提高性能,因为动态扩容会损耗性能

HashMap(int initialCapacity, float loadFacotor):可以自定义初始容量和加载因子

4. 扩容会存在条件竞争,多线程下扩容,会造成死锁,因此HashMap不是线程安全的

四、数据结构

1. HashMap是由数组和链表组合实现的,一个数组的值加上其链表,叫做一个散列桶

2. 数组初始容量为initalCapacity

3. 链表的每一项为Entry,Entry为HashMap的内部类,包含4个字段:key(键),value(值),next(下一节点),hash(hash值)

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        .......
}

五、存储实现过程:put(key,value)

1. key可以为null,当key为null时,调用putForNullKey方法

2. 通过key的hashCode()方法计算key的hash值,根据hash值确认在table数组中的索引位置

3. 如果table中没有元素,直接插入

4. 否则遍历Entry,通过equals()方法比较key,如果不同则插入链表头,调用addEntry方法;否则替换掉旧值,所以没有相同的key

5. 如果链表长度超过了阈值8,就把链表转成红黑树,链表长度低于6,就把红黑树转回链表;

a. 使用红黑树,因为红黑树是平衡二叉树

b. 不使用二叉树,因为二叉树在极端的情况下,会变成一条线性结构

6. 如果数组容量超过了装载因子,就需要扩容

public V put(K key, V value) {
        //当key为null,调用putForNullKey方法,保存null与table第一个位置中,这是HashMap允许为null的原因
        if (key == null)
            return putForNullKey(value);
        //计算key的hash值
        int hash = hash(key.hashCode());                  ------(1)
        //计算key hash 值在 table 数组中的位置
        int i = indexFor(hash, table.length);             ------(2)
        //从i出开始迭代 e,找到 key 保存的位置
        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            //判断该条链上是否有hash值相同的(key相同)
            //若存在相同,则直接覆盖value,返回旧value
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;    //旧值 = 新值
                e.value = value;
                e.recordAccess(this);
                return oldValue;     //返回旧值
            }
        }
        //修改次数增加1
        modCount++;
        //将key、value添加至i位置处
        addEntry(hash, key, value, i);
        return null;
    }

六、读取实现过程:get(key)

1. 根据key的hash值找到Entry

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

2. 通过keys.equals()方法找到链表中正确的位置

七、如何减少碰撞

1. 使用扰动函数,使不相等的对象返回不同的hashcode

2. 使用包装类(例如Integer, String)作为key,String是不可变的,作为key,键值就是不变的,可以缓存其hashcode

八、 HashMap和HashTable的区别(几乎可以等价,HashTable已经过时,不推荐使用):都实现了Map接口

1. HashMap不是同步(synchronized)的,所以不是线程安全的,即多线程环境下可能有问题

a. 可以用ConcurrentHashMap解决线程安全问题

b. 用Collections.synchronizeMap(hashMap)方法解决线程安全问题

2. HashTable是同步的,是线程安全的,但在单线程比较慢,同步会加同步锁,意味着一次仅有一个线程能够更新对象

3. HashMap可以接受key为null,而HashTable不行

4. HashMap的迭代器(Iterator)是fail-fast迭代器,可能在多线程环境下更改结构时出ConcurrentModificationException异常;而HashTable的迭代器(Enumerator)不是

5. HashMap不能保证元素的排列次序,所以次序可能会变!!!可以使用LinkedHashMap代替

九、使用线程安全的集合类:ConcurrentHashMap和CopyOnWriteArrayList

1. 同步的集合类(HashTable和Vector),同步的封装类(使用Collections.synchronizeMap()和Collections.synchronizeList()方法返回的对象)是线程安全的

2. 但是不适合高并发的系统,它们仅有单个同步锁,并且对整个集合加锁,以及为了防止ConcurrentModificationException异常经常在迭代的时候将集合锁定一段时间

十、ConcurrentHashMap和HashTable的区别

a. HashTable的大小增加到一定的时候,性能会急剧下降,因为迭代的时候会对整个对象锁定很长时间

b. ConcurrentHashMap引入了分割(Segmentation),在迭代的过程中,仅仅锁定map的某个部分,而HashTable是锁住整个Map,因此比HashTable性能好

十一、HashMap与HashSet的区别,存储不同数据类型

1. HashMap实现了Map接口,存储键值对,不允许有重复的Key;HashSet实现了Set接口,存储对象,不允许有重复的值

2. 添加元素:HashMap: put(Object key, Object value);  HashSet: add()

原文地址:https://www.cnblogs.com/june0816/p/7295718.html