jdk1.7-HashMap原理分析
时间:2022-07-25
本文章向大家介绍jdk1.7-HashMap原理分析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
jdk1.7-HashMap原理
jdk1.7-HashMap的简介
hashMap的初步使用就不一一赘述了,很多文章都能找的到相应的用法,这里主要讲讲hashMapjdk1.7版本和jdk1.8版本有什么区别:
- jdk1.7采用的是数组+单向链表
- jdk1.8采用的是数组+红黑树,红黑树的效率高于单向链表
我们主要讲解的是jdk1.7hashMap,1.8的之后也会更新
这里要说一下,(JavaScript第六版)ES6中map其实和jdk1.7的HashMap的实现原理相当一致,但是缺少了一步扩容。如果有小伙伴能找到ES6中的扩容方法,可以提供一下。
jdk1.7-HashMap实现原理
- hashMap的底层存储结构是数组+链表
- 此处我们将数组想象成一个桶,易于理解
- 根据存入数据的key采用hash算法去计算出一个hash值
- 判断桶是否需要扩容
- 之后再将数据存入桶的相应位置处
仿写源码
根据思路仿写处源码
public class ExtHashMap<K, V> implements ExtMap<K, V> {
// 定义HashMap底层存储结构。一开始不初始化,懒加载
private Node<K, V>[] table = null;
// 数组扩容的负载因子,当负载因子太小,会造成频繁扩容。负载因子过大导致链表过长
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
// table的初始化容量
private static int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// hashMap的实际长度
private int size;
/**
* @Description:判断key是否相同
*/
private boolean keyEqual(Node node, K k) {
return node.key.equals(k) && node.key == k;
}
/**
* @Description:计算当前key的hash值
*/
private int calculateHash(K k, int length) {
return k.hashCode() % length;
}
/**
* @Description:hashMap进行添加值的方法
*/
public V put(K k, V v) {
// 判断table是否初始化
if (table == null) {
table = new Node[DEFAULT_INITIAL_CAPACITY];
}
// 判断是否需要扩容
if (size >= (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR)) {
resize();
}
// 将新元素添加到数组中
Node newNode = new Node(k, v, null);
// 获取hashCode值,并计算hash值
int hash = calculateHash(k, DEFAULT_INITIAL_CAPACITY);
// 获取table中是否有元素
Node node = table[hash];
if (node == null) {
// 当前table下标处为空,需添加新的链表
table[hash] = newNode;
size++;
} else {
// 当前table下标处具有链表
if (keyEqual(node, k)) {
// key相同的情况下,进行数据覆盖
node.setValue(v);
} else {
// key 不同,需要将元素添加到链表最前端
newNode.next = node;
table[hash] = newNode;
size++;
}
}
return v;
}
/**
* @Description:数组进行扩容的方法
*/
private void resize() {
// 创建新的table,并且进行数组的扩容
Node<K, V>[] newTable = new Node[DEFAULT_INITIAL_CAPACITY << 1];
// 将table中的数据转移到newTable中,同时进行hash重排
for (int i = 0; i < size; i++) {
// 获取老的node
Node oldNode = table[i];
// 避免出现脏数据
table[i] = null;
while (oldNode != null) {
// 获取oldNode的下一个
Node oldNodeNext = oldNode.next;
// 根据新的长度计算新的hash值
int newHash = calculateHash((K) oldNode.key, DEFAULT_INITIAL_CAPACITY << 1);
// 在链表最前面添加oldNode
oldNode.next = newTable[newHash];
newTable[newHash] = oldNode;
oldNode = oldNodeNext;
}
}
// 将newTable赋值给table,并且修改默认长度值
DEFAULT_INITIAL_CAPACITY <<= 1;
table = newTable;
// 方便gc回收
newTable = null;
}
/**
* @Description:hashMap根据key获取value的方法
*/
public V get(K k) {
Node node = getNode(k);
return node == null ? null : (V) node.value;
}
/**
* @Description:根据key获取某个node
*/
public Node getNode(K k) {
// 计算hash值
int hash = calculateHash(k, DEFAULT_INITIAL_CAPACITY);
Node node = table[hash];
while (node != null) {
if (keyEqual(node, k)) {
return node;
}
}
return null;
}
/**
* @Description:返回hashMap的实际长度
*/
public int size() {
return size;
}
void print() {
for (int i = 0; i < table.length; i++) {
Node<K, V> node = table[i];
System.out.print("下标位置[" + i + "]");
while (node != null) {
System.out.print("[ key:" + node.getKey() + ",value:" + node.getValue() + "]");
node = node.next;
}
System.out.println();
}
}
/**
* @Description:节点
*/
class Node<K, V> implements Entry<K, V> {
K key;
V value;
Node<K, V> next;
private Node(K key, V value, Node<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return value;
}
}
}
- android 热修补之andfix实践
- ios 设置label的高度随着内容的变化而变化
- Android字体大小怎么自适应不同分辨率?
- iOS textfield实现一行的数字限制,超出进行弹框
- android https安全连接
- 第二章 正则表达式位置匹配攻略
- 从网络上下载省份城市名称并存入文件然后进行读取省份城市
- android 应用模式之mvp
- Android网络请求框架之Retrofit实践
- iOS 跳转到应用所在的App Store市场
- 第一章 正则表达式字符匹配攻略
- js最新手机号码、电话号码正则表达式
- iOS afnetworking最新版报错 没有AFHTTPRequestOperationManager类了
- Android Service学习之本地服务
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 几个小细节帮你提升java代码运行效率
- spring data操作ES简直不能再香
- 国防科大提出基于可变形三维卷积(D3Dnet)的视频超分辨,代码已开源
- 不要被kafka的异步模式欺骗了
- 给你总结几个ES下最容易踩的坑
- ES系列之利用filter让你的查询效率飞起来
- ES主分片和副本数据大小不一样的情况
- 关于kibana的可视化可能都在这篇文章里了
- ES分页看这篇就够了
- ES系列之原来查看文档数量有这么多姿势
- ES系列之嵌套文档和父子文档
- ES系列之一文带你避开日期类型存在的坑
- ES系列之原来ES的聚合统计不准确啊
- fastjson远程代码执行漏洞问题分析
- 数据库连接池的原理没你想得这么复杂