(原)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。

时间: 2024-08-09 22:04:20

(原)HashMap之java8新特性的相关文章

java8 新特性精心整理

前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和 JVM 等方面的十多个新特性.在本文中我们将学习这些新特性,并用实际的例子说明在什么场景下适合使用. 引用:本文参考了这两篇文章,加以自己的理解,整理成一份最容易理解的 Java8 新特性文章,有少部分章节可能内容一致,但绝对不是抄袭,只是为了文章的完整性,大部分常用的地方加了我自己的理解和示例. https://blog.

java8 新特性精心整理(全)

前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和 JVM 等方面的十多个新特性.在本文中我们将学习这些新特性,并用实际的例子说明在什么场景下适合使用. 引用:本文参考了这两篇文章,加以自己的理解,整理成一份最容易理解的 Java8 新特性文章,有少部分章节可能内容一致,但绝对不是抄袭,只是为了文章的完整性,大部分常用的地方加了我自己的理解和示例. https://blog.

Java精品高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,视频教程

36套精品Java架构师,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,P2P金融项目,大型分布式电商实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Elasticsearch,Redis.ActiveMQ.Nginx.Mycat.Spring.MongoDB.ZeroMQ.Git.Nosql.Jvm.Mecached.Netty.Nio.Mina.java8新特性,P2P金融项目,程序设计,

Java高级架构,java8新特性,P2P金融项目,程序设计,功能设计,第三方支付,web安全视频教程

36套精品Java精品高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,大型分布式电商实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Elasticsearch,Redis.ActiveMQ.Nginx.Mycat.Spring.MongoDB.ZeroMQ.Git.Nosql.Jvm.Mec

Java8新特性浅析

欢迎阅读我编写的Java 8介绍.本教程将带领你一步一步地认识这门语言的新特性.通过简单明了的代码示例,你将会学习到如何使用默认接口方法,Lambda表达式,方法引用和重复注解.看完这篇教程后,你还将对最新推出的API有一定的了解,例如:流控制,函数式接口,map扩展和新的时间日期API等等. 允许在接口中有默认方法实现 Java 8 允许我们使用default关键字,为接口声明添加非抽象的方法实现.这个特性又被称为扩展方法.下面是我们的第一个例子: 1 2 3 4 5 6 7 interfac

Java核心技术之Java8新特性-Lambda表达式

1 总体说明 Java8新特性概述 函数式接口 Lambda表达式(闭包) 2 Java8新特性概述 Oracle公司于2014年3月发布了Java8正式版,该版本是自JDK5.0以来最具革命性的版本. Java8为Java语言.编译器.类库和JVM带来了大量的新特性.接下来的内容将会详细说明Java8在Java语言方面的新特性以及它们的使用场景. 3 函数式接口 Java8引入的一个核心概念是函数式接口(Functional Interfaces):如果一个接口定义一个唯一的抽象方法,那么这个

Java8新特性概览

Java8新特性简介 a)速度更快 1.对于JVM内存模型的新定义,将方法区从堆内存中移除,以前HotSpot JVM堆内存分为三块:1.年轻代  2.年老代  3.持久代(即方法区),点击回顾 取而代之的是 MetaSpace 元空间  - 直接依赖于物理内存,而非由JVM向系统申请 2.对于HashMap等,使用红黑树来更好地解决哈希碰撞后带来的性能问题,以及大量使用非锁的CAS算法来支持并发操作 b)代码更少 Lambda表达式 c)强大的Stream API 操作Java程序中的数据,就

java8新特性,P2P金融项目,程序设计,功能设计,架构师视频教程

36套精品Java精品高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,大型分布式电商项目实战视频教程 视频课程包含: 36套Java精品高级课架构课包含:java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,架构设计,web安全,高并发,高性能,高可用,高可扩展,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,工作流,程序调优,负载均衡,S

java8新特性:对map集合排序

一.简单介绍Map 在讲解Map排序之前,我们先来稍微了解下map.map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,Hashtable以及LinkedHashMap等.其中这四者的区别如下(简单介绍): HashMap:我们最常用的Map,HashMap是无序的,它根据key的HashCode 值来存储数据,根据key可以直接获取它的Value,同时它具有很快的访问速度.HashMap最多只允许一条记录的key值为Null(多条会覆盖);允许多条记录的Value为