Java 8中的HashMap性能改进

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):

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;}
}

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

2

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

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

3

即使使用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363840.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css 汉字注音,日本语片假名

代码 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2 <html xmlns"http://www.w3.org/1999/xhtml">3 4 <head>5 <meta content"te…

Microsoft .NET 框架资源基础 ---摘自:msdn

Chris SellsSells Brothers Consulting 摘要&#xff1a;Chris Sells 讨论无类型清单资源和有类型资源&#xff0c;它们是受 Microsoft .NET 框架支持的两种资源。他定义了这两种资源&#xff0c;并介绍了如何在您自己的应用程序中使用它们。 下载 winforms02202003.exe 示例文…

HDU 5025Saving Tang Monk BFS + 二进制枚举状态

3A的题目&#xff0c;第一次TLE&#xff0c;是因为一次BFS起点到终点状态太多爆掉了时间。 第二次WA&#xff0c;是因为没有枚举蛇的状态。 解体思路&#xff1a; 因为蛇的数目是小于5只的&#xff0c;那就首先枚举是否杀死每只蛇即可。 然后多次BFS&#xff0c;先从起点到第一…

ansible模块之yum、pip、service、corn、user、group

ansible相关模块 yum rpm 和yum 的区别 rpm&#xff1a;全称redhat package manager &#xff08;红帽包管理器&#xff09; 不能解决包之间的依赖关系 yum&#xff1a;可以解决依赖关系yum 源配置[rootlocalhost ~]# cat /etc/yum.repos.d/epel.repo [epel] nameExtra Package…

linux nuttx 环境搭建,ubuntu14.04 nuttx开发环境的搭建

origin: http://blog.csdn.net/hunter168_wang/article/details/529145451. NUTTX编译用 toolchain的下载与配置开发环境&#xff1a;64位Ubuntu 14.04 系统编译NuttX用的toolchain下载地址&#xff1a;https://launchpadlibrarian.net/268330503/gcc-arm-none-eabi-5_4-2016q2…

Elasticsearch-kopf导览

当我需要一个插件来显示Elasticsearch的集群状态时&#xff0c;或者需要深入了解通常为经典插件elasticsearch-head所达到的索引时。 由于有很多建议&#xff0c;而且似乎是非官方的继任者&#xff0c;所以我最近对elasticsearch-kopf进行了更详细的介绍 。 我喜欢它。 我不确…

文字阴影-CSS Text-Shadow

1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2<html xmlns"http://www.w3.org/1999/xhtml">3<head>4<title>文字阴影-CSS Text-Shad…

javascript动态创建radio button元素支持IE/Firefox

我们都知道在IE中创建表单元素可以有三种方式varoInput document.createElement("input");varoInput document.createElement("<input />");varoInput document.createElement("<input name />");在Firefox里面仅支持varoInput docu…

C# 只允许运行一个程序实例

using System; using System.Windows.Forms; using System.Runtime.InteropServices;//使用DllImport的必须。 using System.Diagnostics;//引入Process 类namespace 命名空间 {static class Program{private const int WS_SHOWNORMAL 1;[DllImport("User32.dll")]p…

如何在Android Studio中添加RecyclerView-v7支持包

首先右键你的项目然后选择 open module Settings 之后会出现这个界面&#xff0c;选择 Modules 下的app ,>>> Dependencies >>>点击右边的“” 选择第一项Library dependency 出现此界面&#xff0c;然后选择你所需要的 转载于:https://www.cnblogs.com/lcx9…

linux 时间会跳吗,linux系统时间暂时跳跃

此脚本将告诉您何时发生时间漂移以及进程树中的差异,如果这是由更改系统时间的进程引起的,则应该有助于识别此情况.它将打印到终端并登录到当前工作目录中的timedrift.log.#!/bin/basholdTime"$(date %s)"oldPsOutput"$(ps faux)"while true; dosleep 1;cu…

您从未听说过的Java 8的10个功能

Lambdas lambdas lambdas。 这是您在谈论Java 8时所听到的所有信息。但这只是一部分。 Java 8具有许多新功能-有些是功能强大的新类和习惯用语&#xff0c;而另一些则是从一开始就应该存在的功能。 我想介绍十个新功能&#xff0c;我认为这些功能绝对是值得了解的小宝石。 您肯…

浮动层图片鼠标指针移到自动放大

html code:1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2<html xmlns"http://www.w3.org/1999/xhtml">3<head>4<title>缔友计算机信…

透明的iframe

<iframe allowtransparency"true" src"about: " οnlοad"style.display block;"> 只要是两个属性&#xff1a; 1&#xff09; allowtransparency"true" 2&#xff09; src"about: " 或iframe 的风页设置成 在IE下…

koa2 mongdb 做后端接口的小demo

现在前端全栈里面有一种技术栈比较火 前端使用 vue 或者react 后端使用 koa2 mysql数据库 或者mongdb做数据储存 但是基本这样的全栈教程 都要收费 收费就收费吧 但是 有没有遇到非常好的教程 于是 准备硬着头皮看别人项目的源码 自己摸索 一步一步完成 koa mongdb的后端学习…

字符大小端aix linux,long, unsigned long不是跨平台的(慎用)

项目中用到long、long long等字段&#xff0c;遇到一些问题。先说得到的一些结论&#xff1a;大小端&#xff1a;Windows、Linux是小端&#xff0c;AIX是大端。sizeof(指针类型)程序位数/8。long、unsigned long不是跨平台的&#xff0c;一定要慎用。自己写了程序测试各平台下(…

Java构建工具:Ant vs. Maven vs Gradle

最初&#xff0c;Make是唯一可用的构建工具。 后来通过GNU Make进行了改进。 但是&#xff0c;从那时起&#xff0c;我们的需求增加了&#xff0c;结果&#xff0c;构建工具也不断发展。 JVM生态系统主要由三个构建工具组成&#xff1a; 常春藤的 Apache Ant 马文 摇篮 An…

redis问题与解决思路

问题现象&#xff1a; 集群状态 1主 2从&#xff0c;主没有开启bgsave&#xff0c;从开启bgsave。现象所有redis可以访问&#xff0c;进行操作。主不断开始bgsave 1从停止bgsave。 主日志报错# Connection with slave XXXX lost. 从日志报错# Timeout receiving bulk data from…

Asp.Net中用javascript实现弹出窗口永远居中

//Asp.Net中用javascript实现弹出窗口永远居中functionShowDialog(url) { var iWidth600; //模态窗口宽度 var iHeight500;//模态窗口高度 var iTop(window.screen.height-iHeight)/2; var iLeft(window.screen.width-iWidth)/2; window.open(url,"Detail"…

Linux Vim 光标错位,技术|Vim 复制粘帖格式错乱问题的解决办法

有时候&#xff0c;复制文本(尤其是代码)到 Vim&#xff0c;会出现格式错乱的问题。看样子&#xff0c;应该是自动缩进惹得祸。本文不去深究原因&#xff0c;直接给出解决方法。1. paste 模式运行如下命令&#xff0c;进入 paste 模式&#xff1a;:set paste进入 paste 模式后&…