HashMap put、get方法源码分析
HashMap.java的实现是面试必问的问题。
JDK版本
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode)
1. HashMap节点的封装
Node<K, V>
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
//.....
}
hash值算法
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
put null的key,计算hash值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
ok,重点分析一下扩容
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
首先说几点
1. 找tab位置,使用的key.hash() & (capacity - 1)
注意这里用的是位运算与操作,这里与的是capacity - 1,这样结果就不可能超过capactity - 1。不仅保证了结果正确,性能也提供了很多
2. 扩容大小为原来的大小左移一位,即扩大为原来的两倍
扩容主要的移动原来的元素,到新的数组里面
这里源码使用了比较好的方法,用key的hash值与capacity进行与,注意不是capacity - 1。如果与的结果是0,说明在新数组中的位置为原来的位置,不用变。如果结果不为0,最高为1,即capacty,说明在新数组中的位置为原来位置 + 原来的capacity
以前面试也被问道过,当时自信满满的胡说。自认为了解的很清楚,这里纠正一下
1. 封装的节点为Node,不是Entity
2. 扩容为大小为原来的两倍
3. 计算在数组位置,不是取余,是采用位操作。hashcode & capactiy - 1,注意这里的 - 1操作
4. put null的key,这里是可以的,null的hash值为0
5. key的计算为object.hashcode() ^ (object.hashcode >>> 16)
先到这
p.s.https://tech.meituan.com/java-hashmap.html这个写的不错
HashMap put、get方法源码分析的更多相关文章
- hashmap的put方法源码分析
put主源码如下: public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = ha ...
- 【转】HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- HashMap实现原理及源码分析之JDK8
继续上回HashMap的学习 HashMap实现原理及源码分析之JDK7 转载 Java8源码-HashMap 基于JDK8的HashMap源码解析 [jdk1.8]HashMap源码分析 一.H ...
- java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制
通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...
- 每天学会一点点(HashMap实现原理及源码分析)
HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希 ...
- Java split方法源码分析
Java split方法源码分析 public String[] split(CharSequence input [, int limit]) { int index = 0; // 指针 bool ...
- invalidate和requestLayout方法源码分析
invalidate方法源码分析 在之前分析View的绘制流程中,最后都有调用一个叫invalidate的方法,这个方法是啥玩意?我们来看一下View类中invalidate系列方法的源码(ViewG ...
- Linq分组操作之GroupBy,GroupJoin扩展方法源码分析
Linq分组操作之GroupBy,GroupJoin扩展方法源码分析 一. GroupBy 解释: 根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值. 查询表达式: var ...
- HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- HashMap实现原理及源码分析(JDK1.7)
转载:https://www.cnblogs.com/chengxiao/p/6059914.html 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技 ...
随机推荐
- 【python】使用python smtplib库发邮件添加cc,bcc
#!/usr/bin/env python# -*- coding: utf-8 -*- '''@author@mail @date 2017/03/16 发送邮件'''import smtplibf ...
- yum及RPM安装
yum及RPM安装 基本说明: 1.yum相当于windows上面的360软件中心 2.yum是redhat系列发行版的软件安装命令 debian系统用的是apt-get 3.yum安装软件的来源得存 ...
- 【guava】字符串操作
一,Strings类 public void testStrings(){ Strings.isNullOrEmpty("");//返回true Strings.nullToEmp ...
- Python3之random模块
一.简介 ramdom模块提供了一个随机数的函数:random() 它可以返回一个随机生成的实数,范围在[0,1)范围内.需要注意的是random()是不能直接访问的,需要导入模块random才可以使 ...
- P2053 [SCOI2007]修车 费用流
$ \color{#0066ff}{ 题目描述 }$ 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这M ...
- 删除outlook2007账户
开始-设置-控制面板-邮件-显示配置文件-删除-确定
- Query on a tree 树链剖分 [模板]
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...
- Jmeter函数作用域实时取值覆盖[针对HTTP Request等控制器]
jmeter的属性和变量可以简单理解为编程里面的全局变量和局部变量.属性是全局可见,可以跨线程组传递调用,而变量基本上只能存在于一个线程组中(在测试计划定义的变量也是可以跨线程组传递的).同线程组内的 ...
- struts2学习笔记(一)—— struts2介绍及入门程序
一.struts2概述 1.1 什么是struts2? 在介绍Struts2之前,先来认识一下Struts1.Struts1是最早的基于MVC模式的轻量级web框架,他能够合理的划分代码结构,并包含了 ...
- Android 生成xml文件及xml的解析
1.生成xml文件的两种方式 (1)采用拼接的方式生成xml(不推荐使用) (2)利用XmlSerializer类生成xml文件 package com.example.lucky.test52xml ...