我所理解的HashMap

HashMap介绍

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的(后面会讲到)。

简单讲就是:影响HashMap的性能就是初始容量和加载因子,当数据达到当前容量乘以加载因子之后,机会对HashMap进行两倍的扩容(rehash),默认的加载因子是0.75,如果加载因子过大,HashMap数据过多,导入查询插入会变慢,所以当设置初始容量的时候需要考虑好数据,减少rehash

构造函数(基于JDK1.8)
public HashMap(int initialCapacity, float loadFactor) {
       //如果初始容量小于零会抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        //如果初始容量>最大容量值 初始容量会默认等会最大值      MAXIMUM_CAPACITY = 1 << 30
        if (initialCapacity > MAXIMUM_CAPACITY) {
            initialCapacity = MAXIMUM_CAPACITY;
        } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
       //如果初始容量<默认值    复制给最小默认值     DEFAULT_INITIAL_CAPACITY = 4;
            initialCapacity = DEFAULT_INITIAL_CAPACITY;
        }
       //判断加载因子是不是<0的非数字
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        // Android-Note: We always use the default load factor of 0.75f.

        // This might appear wrong but it's just awkward design. We always call
        // inflateTable() when table == EMPTY_TABLE. That method will take "threshold"
        // to mean "capacity" and then replace it with the real threshold (i.e, multiplied with
        // the load factor).
        threshold = initialCapacity;
        init();
    }
    //init为空方法
  /**
     * Initialization hook for subclasses. This method is called
     * in all constructors and pseudo-constructors (clone, readObject)
     * after HashMap has been initialized but before any entries have
     * been inserted.  (In the absence of this method, readObject would
     * require explicit knowledge of subclasses.)
     */
    void init() {
    }

继承关系
public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

HashMap的api

void                 clear()
Object               clone()
boolean              containsKey(Object key)
boolean              containsValue(Object value)
Set<Entry<K, V>>     entrySet()
V                    get(Object key)
boolean              isEmpty()
Set<K>               keySet()
V                    put(K key, V value)
void                 putAll(Map<? extends K, ? extends V> map)
V                    remove(Object key)
int                  size()
Collection<V>        values()
数据在HashMap中的存储方式

先来个图了解一下
这里写图片描述

通过源码put方法来解释此图

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
        //如果key==null就添加到table第一个
            return putForNullKey(value);
        int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
        //获取当前hash在table中的位置
        int i = indexFor(hash, table.length);
        //判断当前位置是否有相同的key
        for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //为什么要比较e.hash==hash因为hash值相同key不一定相同,key相同hash一定相同
            //如果遇到相同key把最新的value复制给e.value,返回oldvalue,可以通过put方法的返回是是否为null来判断key是否相同
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

private V putForNullKey(V value) {
        for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    //新的数据添加过程
    void addEntry(int hash, K key, V value, int bucketIndex) {
      //如果hashmap数量大于规定的大小就rehash,扩容
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);  //扩容到当前的两倍大小
            hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }
   //具体添加新数据的方法
  void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMapEntry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
        size++;
    }
  1. 新的数据的产生。这是一个非常优雅的设计。系统总是将新的Entry对象添加到bucketIndex处。如果bucketIndex处已经有了对象,那么新添加的Entry对象将指向原有的Entry对象,形成一条Entry链,但是若bucketIndex处没有Entry对象,也就是e==null,那么新添加的Entry对象指向null,也就不会产生Entry链了。
  2. 随着HashMap中元素的数量越来越多,发生碰撞的概率就越来越大,所产生的链表长度就会越来越长,这样势必会影响HashMap的速度,为了保证HashMap的效率,系统必须要在某个临界点进行扩容处理。该临界点在当HashMap中元素的数量等于table数组长度*加载因子。但是扩容是一个非常耗时的过程,因为它需要重新计算这些数据在新table数组中的位置并进行复制处理。所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。
HashMap如果解决碰撞的
  1. 有上面提到过通过链表来解决遇到相同hash值的数据此方法叫做”拉链法”
  2. 再hash法,当遇到相同的hash值再hash
术语解释
  1. 哈希桶:简单说就是table[0],table[1],table[i]代表的数据

本人通过查询博客,以及自己查看代码来理解的HashMap的实现原理,可能有不妥之处,希望各位小哥,大神给与指正,谢谢了!不喜勿喷

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值