(原)HashMap之java8新特性
首先说一下HashMap存储结构,数组、链表、树这三种数据结构形成了hashMap。
存储结构下图所示,根据key的hash与table长度确定table位置,同一个位置的key以链表形式存储,超过一定限制链表转为树。
数组的具体存取规则是tab[(n-1) & hash],其中tab为node数组,n为数组的长度,hash为key的hash值。
//链表中数据的临界值,如果达到8,就进行resize扩展,如果数组大于64则转换为树.
static final int TREEIFY_THRESHOLD = 8;
//如果链表的数据小于6,则从树转换为链表.
static final int UNTREEIFY_THRESHOLD = 6;
//如果数组的size大于64,则把链表进行转化为树
static final int MIN_TREEIFY_CAPACITY = 64

//根据key匹配Node,如果匹配不到key,则返回defaultValue
@Override
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
//根据key匹配Node,如果匹配不到则增加key-value,返回null,如果匹配到Node,如果oldValue不等于null则不进行value覆盖,返回oldValue
@Override
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
//根据key匹配node,如果value也相同则删除
@Override
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
//根据key匹配node,如果value也相同则使用newValue覆盖返回true,否则返回false
@Override
public boolean replace(K key, V oldValue, V newValue) {
Node<K,V> e; V v;
if ((e = getNode(hash(key), key)) != null &&
((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
e.value = newValue;
afterNodeAccess(e);
return true;
}
return false;
}
//根据key做匹配Node,(匹配不到则新建然后重排)如果Node有value,则直接返回oldValue,如果没有value则根据Function接口的apply方法获取value,返回value。
//Function接口的apply的入参为key,调用computeIfAbsent时重写Function接口可以根据key进行逻辑处理,apply的返回值即为要存储的value。
@Override
public V computeIfAbsent(K key,
Functionsuper K, ? extends V> mappingFunction) {
if (mappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
if ((first = tab[i = (n - 1) & hash]) != null) {
//如果已经转为树,按照树的规则进行处理
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
//查找整个链表,找到对应的key
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
V oldValue;
if (old != null && (oldValue = old.value) != null) {
afterNodeAccess(old);
return oldValue;
}
}
//根据重写逻辑计算返回value
V v = mappingFunction.apply(key);
if (v == null) {
return null;
} else if (old != null) {
old.value = v;
afterNodeAccess(old);
return v;
}
else if (t != null)
t.putTreeVal(this, tab, hash, key, v);
else {
//如果匹配不到则table加入数据
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
return v;
}
//V computeIfPresent(K key,BiFunction remappingFunction):根据key做匹配,如果匹配不上则返回null,匹配上根据BiFunction的apply方法获取value,返回value。BiFunction接口的apply的入参为key、oldValue,调用computeIfPresent时重写Function接口可以根据key和oldValue进行逻辑处理,apply的返回值如果为null则删除该节点,否则即为要存储的value。
public V computeIfPresent(K key,
BiFunctionsuper K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
Node<K,V> e; V oldValue;
int hash = hash(key);
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
//使用key和原value作为入参
V v = remappingFunction.apply(key, oldValue);
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
}
else
removeNode(hash, key, null, false, true);
}
return null;
}
//V compute(K key,BiFunction remappingFunction):根据key做匹配,根据BiFunction的apply返回做存储的value。匹配到Node做value替换,匹配不到新增node。apply的返回值如果为null则删除该节点,否则即为要存储的value。
@Override
public V compute(K key,
BiFunctionsuper K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
if ((first = tab[i = (n - 1) & hash]) != null) {
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
}
V oldValue = (old == null) ? null : old.value;
//使用key和原value作为入参
V v = remappingFunction.apply(key, oldValue);
if (old != null) {
if (v != null) {
old.value = v;
afterNodeAccess(old);
}
else
removeNode(hash, key, null, false, true);
}
else if (v != null) {
if (t != null)
t.putTreeVal(this, tab, hash, key, v);
else {
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
}
return v;
}
// V merge(K key, V value,BiFunction remappingFunction):功能大部分与compute相同,不同之处在于BiFunction中apply的参数,入参为oldValue、value,调用merge时根据两个value进行逻辑处理并返回value。
@Override
public V merge(K key, V value,
BiFunctionsuper V, ? super V, ? extends V> remappingFunction) {
if (value == null)
throw new NullPointerException();
if (remappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
if ((first = tab[i = (n - 1) & hash]) != null) {
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
}
if (old != null) {
V v;
if (old.value != null)
//使用新老value作为入参
v = remappingFunction.apply(old.value, value);
else
v = value;
if (v != null) {
old.value = v;
afterNodeAccess(old);
}
else
removeNode(hash, key, null, false, true);
return v;
}
if (value != null) {
if (t != null)
t.putTreeVal(this, tab, hash, key, value);
else {
tab[i] = newNode(hash, key, value, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
}
return value;
}
// void forEach(BiConsumer action):调用此方法时实现BiConsumer接口重写void accept(Object o, Object o2)方法,其中o为key,o2为value,可根据自己的实现对map中所有数据进行处理。
@Override
public void forEach(BiConsumersuper K, ? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key, e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
// void replaceAll(BiFunction function):调用此方法时重写BiFunction的Object apply(Object o, Object o2)方法,其中o为key,o2为value,根据重写方法逻辑进行重新赋值。
@Override
public void replaceAll(BiFunctionsuper K, ? super V, ? extends V> function) {
Node<K,V>[] tab;
if (function == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
e.value = function.apply(e.key, e.value);
}
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
computeIfAbsent、computeIfPresent、compute对比
computeIfAbsent:如果key已存在,返回oldVlaue;不存在创建,返回新创建value
computeIfPresent:如果key不存在,返回null;如果已存在,value为null则删除此节点,不为null替换节点value并返回此value。
compute:如果key不存在,新建key进行存储;如果key存在,value为null则删除此节点,不为null替换节点value并返回此value。
(原)HashMap之java8新特性的更多相关文章
- java8 新特性精心整理
前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和 JVM 等方面的十多个新特 ...
- java8 新特性精心整理(全)
前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和 JVM 等方面的十多个新特 ...
- Java8 新特性之Stream----java.util.stream
这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...
- 干货 | Java8 新特性教程
本教程翻译整理自 https://github.com/winterbe/java8-tutorial 本教程首发自个人网站: https://www.exception.site/java8/jav ...
- Java8新特性之Collectors
参考:Java8新特性之Collectors 在第二天,你已经学习了Stream API能够让你以声明式的方式帮助你处理集合.我们看到collect是一个将管道流的结果集到一个list中的结束操作.c ...
- Java8 新特性之流式数据处理
一. 流式处理简介 在我接触到java8流式处理的时候,我的第一感觉是流式处理让集合操作变得简洁了许多,通常我们需要多行代码才能完成的操作,借助于流式处理可以在一行中实现.比如我们希望对一个包含整数的 ...
- java8新特性--Stream的基本介绍和使用
什么是Stream? Stream是一个来自数据源的元素队列并可以进行聚合操作. 数据源:流的来源. 可以是集合,数组,I/O channel, 产生器generator 等 聚合操作:类似SQL语句 ...
- Java8新特性(一)——Lambda表达式与函数式接口
一.Java8新特性概述 1.Lambda 表达式 2. 函数式接口 3. 方法引用与构造器引用 4. Stream API 5. 接口中的默认方法与静态方法 6. 新时间日期 API 7. 其他新特 ...
- java8新特性:对map集合排序
一.简单介绍Map 在讲解Map排序之前,我们先来稍微了解下map,map是键值对的集合接口,它的实现类主要包括:HashMap, TreeMap, Hashtable以及LinkedHashMap等 ...
随机推荐
- wemall app商城源码中基于PHP的ThinkPHP惯例配置文件代码
wemall doraemon是Android客户端程序,服务端采用wemall微信商城,不对原商城做任何修改,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可随意定制修改.本文分享其中 ...
- swift -- 构造/析构函数
一.构造函数 //当一个类实例化一个对象时候,第一个调用的方法 class Student { //属性 var name = "ser" let age : Int //1.重 ...
- arcgisserver成功发布服务后,浏览服务,无地图显示
软件:ArcMap10.2,ArcgisCatalog10.2 方法:ArcMap10.2添加数据库连接,成功登陆数据库后,拖拽目标图层至Map窗口,对各个图层进行符号化设置 ArcCatalog中找 ...
- .Net程序员学用Oracle系列(22):分析函数(OVER)
1.函数语法 1.1.语法概述 1.2.窗口详解 1.2.1.ROWS 窗口 1.2.2.RANGE 窗口 2.函数用法 2.1.普通统计类函数 2.2.数据排序类函数 2.3.数据分布类函数 2.4 ...
- Maven项目搭建(一):Maven初体验
今天给大家介绍一个项目管理和综合工具:Maven. Maven: maven读作 ['meivin],本意是指可以被信任的领域专家,致力于传播知识(来自于http://en.wikipedia.org ...
- supermap开发webgis的经验
SuperMap 开发WebGIS的经验总结 - 综合课件 - 道客巴巴 http://www.doc88.com/p-743552004620.html
- python多版本的pip共存问题解决办法
python pip 多版本 问题情景 最开始学python的时候用的是py2,且一直用pip来安装库函数.后来py3出来了,所以就装上了,但是一装上出问题了,主要有两个主要的问题.下面将详细说明. ...
- golang的http分析
首先,要认识一个贯穿始终的接口http.Handler type Handler interface { ServeHTTP(ResponseWriter, *Request) } 其中,两个参数 ...
- Access SQL实现连续及不连续Rank排名
一.关于起因 在Excel中我们经常使用Rank函数对数据进行排名操作.而在Access中我们要进行排名是找不到这个Rank函数的,此时我们需要自己书写VBA代码或者建立SQL查询来完成排序操作. 今 ...
- Framework7首页隐藏navbar其他页面显示navbar
Framework7首页隐藏navbar其他页面显示navbar 帮别人解决问题,自己也记录一下, 首页.navbar加.navbar-hidden, 首页.page加.no-navbar, 如果首页 ...