源码分析系列教程(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;
}

总结

目录
相关文章
框架集合之Map集合
框架集合之Map集合
81 0
源码分析系列教程(12) - 手写Map框架(基于JDK1.7)
源码分析系列教程(12) - 手写Map框架(基于JDK1.7)
39 0
|
5月前
|
JavaScript 定位技术
vue 百度地图开发【教程】1. 绘制百度地图(不使用 vue-baidu-map,解决 BMap is undefined)
vue 百度地图开发【教程】1. 绘制百度地图(不使用 vue-baidu-map,解决 BMap is undefined)
442 0
|
7月前
|
存储 算法 C++
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
540 1
|
数据处理 开发者 Python
Google Earth Engine Map教程书中英双语
Google Earth Engine Map教程书中英双语
421 0
|
6月前
|
Dart
Dart之集合详解(List、Set、Map)
Dart之集合详解(List、Set、Map)
|
3月前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
3月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
4月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
4月前
|
Java
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。