java8hashmap

HashMap<K, V>是每个Java程序中快速,通用且无处不在的数据结构。 首先是一些基础知识。 您可能知道,它使用键的hashCode()equals()方法在存储桶之间拆分值。 存储桶(箱)的数量应略高于映射中的条目数,以使每个存储桶仅保留很少(最好是一个)值。 当按键查找时,我们很快确定了存储桶(使用hashCode()模数number_of_buckets模),并且我们的商品在固定时间可用。

这应该已经为您所了解。 您可能还知道,哈希冲突对HashMap性能具有灾难性的影响。 当多个hashCode()值最终出现在同一存储桶中时,这些值将放置在临时链接列表中。 在最坏的情况下,当所有键都映射到同一存储桶时,会将哈希映射退化为链表–从O(1)到O(n)查找时间。 让我们首先对HashMap在Java 7(1.7.0_40)和Java 8(1.8.0-b132)中的正常情况下的行为进行基准测试。 为了完全控制hashCode()行为,我们定义了自定义Key类:

class Key implements Comparable<Key> {private final int value;Key(int value) {this.value = value;}@Overridepublic int compareTo(Key o) {return Integer.compare(this.value, o.value);}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass())return false;Key key = (Key) o;return value == key.value;}@Overridepublic int hashCode() {return value;}
}

Key类行为良好:它覆盖equals()并提供了体面的hashCode() 。 为了避免过多的GC,我缓存了不可变的Key实例,而不是一遍又一遍地创建它们:

public class Keys {public static final int MAX_KEY = 10_000_000;private static final Key[] KEYS_CACHE = new Key[MAX_KEY];static {for (int i = 0; i < MAX_KEY; ++i) {KEYS_CACHE[i] = new Key(i);}}public static Key of(int value) {return KEYS_CACHE[value];}}

现在我们准备进行一些实验。 我们的基准测试将使用连续键空间简单地创建不同大小(10的幂,从1到1百万)的HashMap 。 在基准测试本身中,我们将根据键查找值并测量所需的时间,具体取决于HashMap大小:

import com.google.caliper.Param;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;public class MapBenchmark extends SimpleBenchmark {private HashMap<Key, Integer> map;@Paramprivate int mapSize;@Overrideprotected void setUp() throws Exception {map = new HashMap<>(mapSize);for (int i = 0; i < mapSize; ++i) {map.put(Keys.of(i), i);}}public void timeMapGet(int reps) {for (int i = 0; i < reps; i++) {map.get(Keys.of(i % mapSize));}}}

结果确认HashMap.get()确实是O(1):

有趣的是,在简单的HashMap.get() Java 8平均比Java 7快20%。 整体性能同样令人感兴趣:即使在HashMap有100万个条目,一次查找所用的时间也不到10纳秒,这意味着我的机器上大约有20个CPU周期* 。 令人印象深刻! 但这不是我们要进行基准测试的结果。

假设我们有一个非常差的映射键,它总是返回相同的值。 这是最糟糕的情况,完全HashMap使用HashMap

class Key implements Comparable<Key> {//...@Overridepublic int hashCode() {return 0;}
}

我使用了完全相同的基准来查看它在各种地图尺寸下的行为(注意这是对数对数比例):

预计Java 7的结果。 HashMap.get()的成本与HashMap本身的大小成比例地增长。 由于所有条目都在一个巨大的链接列表中的同一存储桶中,因此查找一个条目平均需要遍历该列表的一半(大小为n)。 因此,O(n)复杂度如图所示。

但是Java 8的性能要好得多! 这是一个对数刻度,因此我们实际上在谈论几个数量级的更好。 在灾难性哈希冲突的情况下,在JDK 8上执行的相同基准会产生O(logn)最坏情况的性能,如将JDK 8单独以对数线性比例显示时,会更好地显示:

即使使用big-O表示法,如此巨大的性能改进背后的原因是什么? 好,在JEP-180中描述了此优化。 基本上,当存储桶变得太大时(当前: TREEIFY_THRESHOLD = 8 ), HashMap用树形图的临时实现动态替换它。 这样,我们不必感到悲观的O(n),而会得到更好的O(logn)。 它是如何工作的? 好吧,以前具有冲突键的条目只是简单地附加到链表中,而后又需要遍历。 现在, HashMap使用哈希码作为分支变量,将列表提升为二叉树。 如果两个散列不同,但最终在同一个存储桶中,则认为一个更大,并向右移动。 如果哈希值相等(如本例所示),则HashMap希望键是Comparable ,以便它可以建立一些顺序。 这不是HashMap密钥的要求,但显然是一种好习惯。 如果密钥不具有可比性,那么在发生大量哈希冲突的情况下,不要指望任何性能提高。

为什么所有这些都那么重要? 知道我们使用的哈希算法的恶意软件可能会处理数千个请求,这些请求将导致大量的哈希冲突。 重复访问此类密钥将严重影响服务器性能,从而有效地导致拒绝服务攻击。 在JDK 8中,从O(n)到O(logn)的惊人跳变将阻止这种攻击向量,也使性能更具预测性。 我希望这将最终说服您的老板升级。


*在Intel Core i7-3635QM @ 2.4 GHz,8 GiB RAM和SSD驱动器上执行的基准,在64位Windows 8.1和默认JVM设置上运行。

翻译自: https://www.javacodegeeks.com/2014/04/hashmap-performance-improvements-in-java-8.html

java8hashmap

java8hashmap_Java 8中的HashMap性能改进相关推荐

  1. Java 8中的HashMap性能改进

    HashMap<K, V>是每个Java程序中快速,通用且无处不在的数据结构. 首先是一些基础知识. 您可能知道,它使用键的hashCode()和equals()方法在存储桶之间拆分值. ...

  2. .NET 3.5 中WCF客户端代理性能改进以及最佳实践

    介绍 在.NET 3.0 SP1(与.NET 3.5一起发布) 中,WCF客户端创建有一个重要的性能改进.对BasicHttpBinding 来说,性能已经接近于创建ASMX代理. ASMX 代理 v ...

  3. Qt 5.12 LTS(长期维护版本)中Qt Quick的性能改进

    我们一直致力于提高Qt的性能和优化其内存消耗.Qt 5.12的一个重点关注是在于减少QML引擎的内存消耗和优化JavaScript性能. 与上一个长期支持版Qt 5.6 LTS相比,Qt 5.9 LT ...

  4. .NET 5 中的正则引擎性能改进(翻译)

    前言 System.Text.RegularExpressions 命名空间已经在 .NET 中使用了多年,一直追溯到 .NET Framework 1.1.它在 .NET 实施本身的数百个位置中使用 ...

  5. 【译】.NET 7 中的性能改进(十一)

    ▲ 点击上方"DotNet NB"关注公众号 回复"1"获取开发者路线图 学习分享 丨作者 / 郑 子 铭 这是DotNet NB 公众号的第215篇原创文章 ...

  6. 【翻译】.NET 5中的性能改进

    [翻译].NET 5中的性能改进 在.NET Core之前的版本中,其实已经在博客中介绍了在该版本中发现的重大性能改进. 从.NET Core 2.0到.NET Core 2.1到.NET Core ...

  7. 【译】.NET 7 中的性能改进(八)

    原文 | Stephen Toub 翻译 | 郑子铭 Mono 到目前为止,我一直提到 "JIT"."GC "和 "运行时",但实际上在.N ...

  8. 【译】.NET 7 中的性能改进(一)

    ▲ 点击上方"DotNet NB"关注公众号 回复"1"获取开发者路线图 学习分享 丨作者 / 郑 子 铭 这是DotNet NB 公众号的第206篇原创文章 ...

  9. JDK1.8中的HashMap

    目录 JDK1.8中的HashMap 问题1:为什么要将1.7中HashMap的链表结构改为红黑树? 问题2:为什么HashMap要用红黑树?红黑树好在哪里. 二叉查找树概述 红黑树概述 红黑树的插入 ...

最新文章

  1. C++ 笔记(12)— 判断(if/if...else/switch、条件运算符)
  2. 透视世界人工智能发展
  3. div中的图像在图像下方有多余的空间
  4. Serverless 在大规模数据处理的实践
  5. 关于c++静态类的说法
  6. 如何在 IDEA 启动多个 Spring Boot 工程实例
  7. java的制造商,国内某通信设备制造商JavaEE开发岗面试题
  8. linux mysql删除用户权限_linuxmysql增加用户,删除用户,以及用户权限_MySQL
  9. JavaScript学习(六十二)—解析选项和序列化选项
  10. Swift 简单的通讯录
  11. 转--global.asax文件(站点计数器)
  12. 车牌识别平台开源(支持蓝牌、绿牌,准确率高达96%)
  13. 基于Esp8266的远程开机棒设计和实现
  14. 齐聚绿城 | 锦江都城酒店聚焦中高端酒店投资新方向
  15. 2023美国大学生数学建模竞赛资料及思路
  16. 转载(工信部):启明信息技术股份有限公司 孙健
  17. MATLAB 数字图像处理---车牌简单识别【亲测有效】
  18. pcDuino指纹识别
  19. java程序设计谢先伟课后答案_最新网课答案沟通心理学见面课2020知到APP
  20. linux下的socket通信

热门文章

  1. 史上最全MySQL 大表优化方案(长文)
  2. JavaFX UI控件教程(三)之Label
  3. MongoDB查询实现 笛卡尔积,Union All 和Union 功能
  4. 集合框架 Queue---BlockingQueue详解
  5. 算法二之树形选择排序
  6. 接口 DataOutput
  7. 《四世同堂》金句摘抄(七)
  8. 《四世同堂》金句摘抄(五)
  9. java中判断 101-200 之间有多少个素数,并输出所有的素数
  10. 当当网头部和尾部——CSS源码