【 HashMap 】

1 底层结构

  • JDK 1.7 HashMap 的底层结构是由 数组+链表 构成的

  • JDK 1.8 HashMap 的底层结构是由 数组+红黑树 构成的

在这里插入图片描述

  • 数组(紫色):hash 数组(桶),数组元素是每个链表的头节点

  • 链表(绿色):解决 hash 冲突,不同的 key 映射到了数组的同一索引处,则形成链表。

2 putget 方法

put() 方法大概过程如下:

  • 如果添加的 key值为 null,那么将该键值对添加到数组索引为0的链表中,不一定是链表的首节点。

  • 如果添加的 key 不为 null,则根据 key 计算数组索引的位置:

    • 数组索引处存在链表,则遍历该链表,如果发现 key 已经存在,那么将新的 value 值替换旧的 value
    • 数组索引处不存在链表,将该 key-value 添加到此处,成为头节点
      get() 方法的大概过程:
  • 如果 keynull,那么在数组索引 table[0] 处的链表中遍历查找 keynullvalue

  • 如果 key 不为 null,根据 key 找到数组索引位置处的链表,遍历查找 keyvalue,找到返回 value,若没找到则返回 null

3 扩容机制

先看一个例子,创建一个 HashMap,初始容量默认为 16,负载因子默认为 0.75,那么什么时候它会扩容呢?

来看以下公式:

实际容量 = 初始容量 × 负载因子

计算可知,16×0.75=12,也就是当实际容量超过 12 时,这个 HashMap 就会扩容。

3.1 初始容量

当构造一个 hashmap 时,初始容量设为不小于指定容量的2的次方的一个数(new HashMap(5), 指定容量为5,那么实际初始容量为8,2^3=8>5),且最大值不能超过2的30次方。

3.2 负载因子

负载因子是哈希数组在其容量自动增加之前可以达到多满的一种尺度。(时间与空间的折衷) 当哈希数组中的条目数超出了加载因子与初始容量的乘积时,则要对该哈希数组进行扩容操作(即resize)。
特点:

1*16=16
0.75*16=12   0.5*16=8

负载因子越小, 容易扩容—容易扩容的时候 产生新的空数组位置

负载因子越小,容易扩容,浪费空间,但查找效率高 链表特别短 减少hash冲突

负载因子越大,不易扩容,对空间的利用更加充分,查找效率低(链表拉长)hash冲突比较多,链表比较长

3.3 扩容过程

HashMap 在扩容时,新数组的容量将是原来的2倍,由于容量发生变化,原有的每个元素需要重新计算数组索引 Index,再存放到新数组中去,这就是所谓的 rehash

3.4 eqauls 方法和 hashCode 方法

如果两个对象相同,那么它们的 hashCode 值一定要相同。也告诉我们重写 equals 方法,一定要重写 hashCode 方法,也就是说 hashCode 值要和类中的成员变量挂上钩,对象相同–>成员变量相同—->hashCode值一定相同

如果两个对象的 hashCode 相同,它们并不一定相同,这里的对象相同指的是用 eqauls 方法比较。

4 基于 Arraylist 实现 HashMap 集合

package com.snow.list;

import java.util.ArrayList;
import java.util.List;

/**
 * 基于Arraylist实现hashMap集合<br>
 * 缺点:性能特别低<br>
 *
 */
public class ExtArraylistHashMap<Key, Value> {

    // MAP 存储容量
    private List<Entry<Key, Value>> tables = new ArrayList<Entry<Key, Value>>();

    // map容器实际容量
    // private int size;
    // key、value

    public void put(Key key, Value value) {
        Entry entry = getEntry(key);
        if (entry != null) {
            // 已经存在
            entry.value = value;
        } else {
            Entry newEntry = new Entry(key, value);
            // 2.调用put的时候,将该hash存储对象存入到Arraylist中
            tables.add(newEntry);
        }

    }

    public Value get(Key key) {
        Entry<Key, Value> entry = getEntry(key);
        return entry == null ? null : entry.value;
    }

    public Entry<Key, Value> getEntry(Key key) {
        // 从头查询到尾做优化### 准
        for (Entry<Key, Value> entry : tables) {
            if (entry.key.equals(key)) {
                return entry;
            }
        }
        return null;
    }

    public static void main(String[] args) {
        ExtArraylistHashMap hashMap = new ExtArraylistHashMap<String, String>();
        hashMap.put("a", "aaaa");
        hashMap.put("b", "bbbb");
        hashMap.put("a", "cccc");// key 相同的情况下, 会覆盖
        System.out.println(hashMap.get("a"));
    }

}

// hash存储对象
class Entry<Key, Value> {

    // hashMap集合存储的key
    Key key;
    // hashMap集合存储的value
    Value value;

    public Entry(Key key, Value value) {
        super();
        this.key = key;
        this.value = value;
    }

}

控制台打印:

cccc

5 基于 LinkedList 实现 HashMap (效率低)

package com.snow.list;

import java.util.LinkedList;

/**
 * 基于LinkedList实现HashMap(效率低)<br>
 * jdk 1.7的时候,hasmap使用数组+链表方式实现<br>
 *
 */
@SuppressWarnings("rawtypes")
public class LinkedListHashMap {

    // Map存放Entry对象
    @SuppressWarnings("unchecked")
    LinkedList<Entry>[] tables = new LinkedList[998];

    // 如果hashCode相同的对象链表结合中
    @SuppressWarnings("unchecked")
    public void put(Object key, Object value) {
        Entry newEntry = new Entry(key, value);
        int hashCode = key.hashCode();// 97
        // hash取模,获取余数
        int hash = hashCode % tables.length;// 998 998以内 00997
        // 1.获取该下标元素,是否有LinkedList
        LinkedList<Entry> entryLinkedList = tables[hash];
        if (entryLinkedList == null) {
            // 没有hash冲突
            entryLinkedList = new LinkedList<Entry>();
            entryLinkedList.add(newEntry);
            // tables数组
            tables[hash] = entryLinkedList;
        } else {
            for (Entry entry : entryLinkedList) {
                if (entry.key.equals(key)) {
                    // equals相等,hashCode 一定相同 说明:是同一个对象
                    entry.value = value;// 直接覆盖
                } else {
                    // hashCode 相同,对象的值不一定相同
                    // 发生hash冲突问题,直接在后面继续添加链表节点
                    entryLinkedList.add(newEntry);
                }
            }

        }
    }

    // hash算法
    // System.out.println(hash);
    // 两个对象做比较的时候,如果hashCode相同,对象的值是否一定相同 不一定相同
    // 两个对象做比较的时候,如果 equals比较相同,对象的值是否一定相同 相同

    // 查询直接使用hash值直接定位在数组那个位置
    public Object get(Object key) {
        int hashCode = key.hashCode();// 97
        // hash取模,获取余数
        int hash = hashCode % tables.length;// 998 998以内 00997
        LinkedList<Entry> linkedList = tables[hash];
        for (Entry entry : linkedList) {
            if (entry.key.equals(key)) {
                return entry.value;
            }
        }
        return tables[hash];
    }

    public static void main(String[] args) {
        LinkedListHashMap linkedListHashMap = new LinkedListHashMap();
        linkedListHashMap.put("a", "aaaa");
        linkedListHashMap.put("a", "ccccc");// 覆盖
        System.out.println(linkedListHashMap.get("a"));
    }

}

// hash存储对象
class Entry<Key, Value> {

    // hashMap集合存储的key
    Key key;
    // hashMap集合存储的value
    Value value;

    public Entry(Key key, Value value) {
        super();
        this.key = key;
        this.value = value;
    }

}

控制台打印:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不知所起 一往而深

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值