HashMap

HashMap

哈希表(线程不安全,没有锁,会导致并发问题—扩容问题)
这里写图片描述
数组的时间复杂度O(n)
这里写图片描述
哈希冲突 哈希函数
构造函数(可以使元素均匀的分布在卡槽里):
1.直接寻址法(f(key)= key)
2.除留余数法(f(p) = p%n)
哈希冲突的解决方法
1.链地址法
构造函数:f(p) = p%16
数组+单项链表
这里写图片描述
2.探测法:pos(n) = f(n)+p(n)
(1)线性探测法:
这里写图片描述
(2)随机探测法:尾随机p(n) = random()
HashMap 的实现是 数组+链表

HashMap基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”及其大小成比例。
hashmap的继承关系:

这里写图片描述
定义hashmap类

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

基本成员变量

{
// 默认的初始容量是16,必须是2的幂。
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 
// 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
    static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认加载因子 0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 空表
    static final Entry<?,?>[] EMPTY_TABLE = {};
// 存储数据的Entry数组,长度是2的幂。
// HashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
//已使用容量的大小
    transient int size;
//阈值,超出需扩容(threshold=容量*加载因子)
    int threshold;
//加载因子
    final float loadFactor;
// HashMap被改变的次数
    transient int modCount;
//HashMap的默认阀值
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

构造函数

// 指定“容量大小”和“加载因子”的构造函数
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);
        // HashMap的最大容量只能是MAXIMUM_CAPACITY
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                loadFactor);

        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        init();
    }
    // 指定“容量大小”的构造函数
       public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    // 默认构造函数
        public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY,DEFAULT_LOAD_FACTOR);
    }
    // 包含Map的构造函数
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);
// 将m中的全部元素逐个添加到HashMap中
        putAllForCreate(m);
    }

返回索引值

// h & (length-1)保证返回值的小于length
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

返回当前已用大小

    public int size() {
        return size;
    }

判断是否为空

    public boolean isEmpty() {
        return size == 0;
    }

获取key对应的value

    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        //获取到Entry实体
        Entry<K,V> entry = getEntry(key);
        return null == entry ? null : entry.getValue();
    }
//获取到键为null的所对应的value值
        private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

HashMap是否包含key

    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

返回“键为key”的键值对

  final Entry<K,V> getEntry(Object key) {
        //HashMap中无元素返回null
        if (size == 0) {
            return null;
        }
// HashMap将key为null的元素存储在table[0]位置,key不为null的则调用hash()计算哈希值
        int hash = (key == null) ? 0 : hash(key);
// 在hash值对应的链表上查找键值等于key的元素,找到返回
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

// 将键值对添加到HashMap中

   public V put(K key, V value) {
        //如果为空表,则需要初始化
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //如果key为null则将该键值对添加到table[0]中
        if (key == null)
            return putForNullKey(value);
        // 若key不为null,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中
        int hash = hash(key);
        //获得其应该存放在数组中的位置
        int i = indexFor(hash, table.length);
        //遍历此位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
// 若key对应的键值对已经存在,则用新的value取代旧的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;
            }
        }
// 若key对应的键值对不存在,则将键值对添加到table中
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

将key为null键值对添加到table[0]位置

    private V putForNullKey(V value) {
        for (Entry<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;
    }

putForCreate()是内部方法,它被构造函数等调用,用来创建HashMap
它和put()不同,put()是对外提供的往HashMap中添加元素的方法

    private void putForCreate(K key, V value) {
        int hash = null == key ? 0 : hash(key);
        int i = indexFor(hash, table.length);
// 若HashMap中存在键值等于key的元素,则替换该元素的value值
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                e.value = value;
                return;
            }
        }
// 若HashMap中不存在键值等于key的元素,则将键值对添加到HashMap中
        createEntry(hash, key, value, i);
    }

将m中的全部元素都添加到HashMap中,该方法被内部的构造HashMap的方法所调用

    private void putAllForCreate(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            putForCreate(e.getKey(), e.getValue());
    }

重新调整HashMap的大小,newCapacity是调整后的单位

    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
// 新建一个HashMap,将旧HashMap的全部元素添加到新HashMap中
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

将HashMap中的全部元素都添加到newTable中

    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

将m的全部元素都添加到HashMap中

    public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;
        if (table == EMPTY_TABLE) {
            inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
        }
        //判断是否需要扩容,当大于阀值就需要扩容
         if (numKeysToBeAdded > threshold) {
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }
        //直接循环put
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

删除键为key元素,并返回以前的值

   public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

清空HashMap,将所有的元素设为null

    public void clear() {
        modCount++;
        Arrays.fill(table, null);
        size = 0;
    }

克隆一个HashMap,并返回Object对象

public Object clone() {
        HashMap<K,V> result = null;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
            // assert false;
        }
        if (result.table != EMPTY_TABLE) {
            result.inflateTable(Math.min(
                (int) Math.min(
                    size * Math.min(1 / loadFactor, 4.0f),
                    // we have limits...
                    HashMap.MAXIMUM_CAPACITY),
               table.length));
        }
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);
        return result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值