源码分析系列教程(11) - 手写Map框架(基于LinkedList)

简介: 源码分析系列教程(11) - 手写Map框架(基于LinkedList)

代码已上传到GitHub,有兴趣的同学可以下载来看看:https://github.com/ylw-github/Java-CodeAnalysis-Demo

1.Map原理

HashMap的底层结构是由数组+链表构成的:

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

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

1.1 put和get方法

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

  1. 如果添加的key值为null,那么将该键值对添加到数组索引为0的链表中,不一定是链表的首节点。
  2. 如果添加的key不为null,则根据key计算数组索引的位置:
    ----- 数组索引处存在链表,则遍历该链表,如果发现key已经存在,那么将新的value值替换旧的value值。
    ----- 数组索引处不存在链表,将该key-value添加到此处,成为头节点。

get()方法的大概过程:

  1. 如果key为null,那么在数组索引table[0]处的链表中遍历查找key为null的value 。
  2. 如果key不为null,根据key找到数组索引位置处的链表,遍历查找key的value,找到返回value,若没找到则返回null。
1.2 扩容机制

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

来看以下公式:

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

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

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

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

特点:

  • 负载因子越小,容易扩容,浪费空间,但查找效率高
  • 负载因子越大,不易扩容,对空间的利用更加充分,查找效率低(链表拉长)
    扩容过程

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

1.3 eqauls方法和hashCode方法
  1. 如果两个对象相同,那么它们的hashCode值一定要相同。也告诉我们重写equals方法,一定要重写hashCode方法,也就是说hashCode值要和类中的成员变量挂上钩,对象相同–>成员变量相同—->hashCode值一定相同。
  2. 如果两个对象的hashCode相同,它们并不一定相同,这里的对象相同指的是用eqauls方法比较。

2.代码实现

package com.ylw.jdk.map;
import java.util.ArrayList;
import java.util.List;
public class ExtArrayListMap<Key, Value> {
    List<Entry<Key, Value>> tables = new ArrayList<Entry<Key, Value>>();
    public void put(Key key, Value value) {
        // 判断key是否已经重复
        Entry existEntry = getEntry(key);
        if (existEntry != null) {
            existEntry.value = value;
            return;
        }
        Entry entry = new Entry<Key, Value>(key, value);
        tables.add(entry);
    }
    public Value get(String key) {
        for (Entry<Key, Value> entry : tables) {
            if (entry.key.equals(key)) {
                return entry.value;
            }
        }
        return null;
    }
    public void remove(Key key) {
        Entry existEntry = getEntry(key);
        if (existEntry != null) {
            tables.remove(existEntry);
        }
    }
    public Entry<Key, Value> getEntry(Key key) {
        for (Entry<Key, Value> entry : tables) {
            if (entry.key.equals(key)) {
                return entry;
            }
        }
        return null;
    }
}
class Entry<Key, Value> {
    public Entry(Key key, Value value) {
        this.key = key;
        this.value = value;
    }
    Key key;
    Value value;
}

总结

目录
相关文章
|
9月前
框架集合之Map集合
框架集合之Map集合
68 0
|
9月前
源码分析系列教程(12) - 手写Map框架(基于JDK1.7)
源码分析系列教程(12) - 手写Map框架(基于JDK1.7)
28 0
|
1月前
|
JavaScript 定位技术
vue 百度地图开发【教程】1. 绘制百度地图(不使用 vue-baidu-map,解决 BMap is undefined)
vue 百度地图开发【教程】1. 绘制百度地图(不使用 vue-baidu-map,解决 BMap is undefined)
56 0
|
3月前
|
存储 算法 C++
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
277 1
|
10月前
|
数据处理 开发者 Python
Google Earth Engine Map教程书中英双语
Google Earth Engine Map教程书中英双语
273 0
|
2月前
|
Dart
Dart之集合详解(List、Set、Map)
Dart之集合详解(List、Set、Map)
43 1
|
2月前
|
存储 JavaScript 前端开发
JavaScript进阶-Map与Set集合
【6月更文挑战第20天】JavaScript的ES6引入了`Map`和`Set`,它们是高效处理集合数据的工具。`Map`允许任何类型的键,提供唯一键值对;`Set`存储唯一值。使用`Map`时,注意键可以非字符串,用`has`检查键存在。`Set`常用于数组去重,如`[...new Set(array)]`。了解它们的高级应用,如结构转换和高效查询,能提升代码质量。别忘了`WeakMap`用于弱引用键,防止内存泄漏。实践使用以加深理解。
|
5天前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
9天前
|
存储
|
1月前
|
存储 安全 Java
Java基础之集合Map
【7月更文挑战第8天】Java中的Map集合以键值对方式存储数据,如`Map&lt;&quot;name&quot;, &quot;张三&quot;&gt;`。Map接口定义了存取、判断、移除等操作,包括`put`、`get`、`containsKey`等方法。HashMap是最常用的实现,基于哈希表,允许null键值,但不保证顺序。其他实现包括同步的Hashtable、处理属性文件的Properties、保持插入顺序的LinkedHashMap、基于红黑树的TreeMap、弱引用的WeakHashMap、并发安全的ConcurrentHashMap和针对枚举优化的EnumMap。
26 4