- 浏览: 499600 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (124)
- hibernate (7)
- spring (18)
- java se (32)
- jdbc (5)
- js (4)
- 数据库 (1)
- ide (2)
- Struts2 (1)
- 有用的链接 (2)
- 开源框架 (2)
- j2ee (2)
- spring mvc (4)
- weblogic (0)
- spring security (4)
- web服务器 (5)
- maven (2)
- 多线程 (12)
- linux (3)
- NIO (1)
- webservice (1)
- guava (5)
- restful (1)
- jenkins (1)
- ios (1)
- ehcache (1)
- jvm (1)
- 深入jvm (2)
- akka发 (1)
- disruptor (3)
- spring boot,异常处理, protobuf (1)
- curator (1)
- zookeeper (1)
最新评论
-
abc08010051:
张延龙地盘 写道多实例下就不行了吧是的,多实例直接上分布式锁
java高并发下的唯一性验证 -
张延龙地盘:
多实例下就不行了吧
java高并发下的唯一性验证 -
a12345531:
s3.getResourceUrl(bucketName, k ...
Amazon AWS S3 上传文件 并获取公用url -
544483342:
楼主请问WorkerEventHandler和EventHan ...
disruptor(一) 单一生产者和WorkPool消费者源码阅读 -
544483342:
请问楼主使用的是什么UML工具啊?
disruptor(一) 单一生产者和WorkPool消费者源码阅读
1、内部存储结构
HashMap的内部存储结构是数组,默认初始化为16长度的Entry[],对于hash冲突采用拉链方法解决,所以它是数组和链表的合体。
2、理解HashMap做到合理使用
假设我们要存放100W数据到HashMap中,那么分到每个链上就有100W/16个数据,显然这样的map是不合理的。
HashMap采用resize数组来增加数据长度降低拉链过长造成的性能影响,当put数据时put方法会判断map中的数据数量是否到达了需要扩充大小的临界点,临界点计算方法是当前数组长度*loadFactor(默认值是0.75),到了临界点后数组长度会扩容到当前数组的2倍。
resize方法相当消耗资源,不过拉链过长已经发生了性能问题,resize也只是性能分摊,以解决后面的性能问题吧,还是值得的,但当我们自己写代码时如果能够预测到HashMap中需要存放多少数据那么我们最好自己定义HashMap数组长度和加载因子loadFactor。
另外不要以为new HashMap(30)就会分配一个长度为30的数组,HashMap会重新给你算一个最优值(一个2的n次幂且大小30,n能取到最小值的数字),32,那么resize临界值就是32*0.75=24,看来指定了长度还是会resize,但构造函数里还有一个参数就是加载因子,修改new HashMap(30, 1),这样我们就指定了一个长度为32,加载因子为1的map,这时我们放30个数据时就不会触发resize了,但如果还继续增加数据,数据个数达到32时就会进行一次resize,不过这也是理所应当。
HashMap为什么要重新算一个2的n次幂长度呢?这就是HashMap中hash的精髓了
put数据过程:首先拿到数据的hashcode,然后再和数组长度-1做与运算找到应存储在数组上的位置,最后进行存储
举例:
数组长度16,存储三个数据的key的hash值分别为1、2、3
用数组长度和1、2、3分别做与操作,得到0、0、0;用数组长度-1和1、2、3做与操作得到1、2、3
用二进制表示会看的更清楚10000&1=0,10000&10=0,10000&11=0;1111&1=1,1111&10=10,1111&11=11
原因就很明显了,只要key有不同的hash值那么冲突的可能性就很平均,查询效率在整体上也会有所提前
除非定长,否则最好不要自己定义加载因子,使用默认就好(new HashMap(30)、new HashMap(64)、new HashMap(128))。
3、key值的hashCode和equals
HashMap的key可以是任意对象,假设我们用在线用户来做key,在线用户对象包括用户id,用户名,用户登录时间,那么显然这个key值中用用户id就可以唯一确定一个值,而加上用户登录时间就不一定了如果一个系统允许用户登录多次就有问题了(可以不太恰当)
这时我们就需要改写hashCode和equals方法,在计算hashCode和equals时只需要考虑用户id这一个属性就可以了
在程序书写过程中也要考虑改写对其它代码造成的影响,如果影响到其它代码了就要重新定义一个key对象了,千万不能在hashCode代码中return 1,那就悲剧了,全在一条拉链上
4、一些源码
void resize(int newCapacity) {// newCapacity为原数据长度的2倍
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);// 将老数组里的数据copy到新数组,是整个HashMap中最耗性能的代码
table = newTable;
threshold = (int)(newCapacity * loadFactor);// 重新定义临界值
}
void transfer(Entry[] newTable) {// 将老数组里的数据copy到新数组,是整个HashMap中最耗性能的代码,要遍历所有数组元素,遍历所有拉链数据,并重新计算存储点,重新拉小链cool
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
public HashMap(int initialCapacity, float loadFactor) {// 构造方法指定长度和加载因子
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)// 直到取到最优值,目的是为了减少hash冲突
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {// 计算key在数组上的存储位置,h为key的hash值,length为数组长度
return h & (length-1);
}
static class Entry<K,V> implements Map.Entry<K,V> {// 静态内部类Entry完成了LinkdList和Entry[]合体后就是HashMap的存储结构了
final K key;
V value;
Entry<K,V> next;
final int hash;
….
}
5、总结
给HashMap一个合理初始大小,注意加载因子的使用,如果没有特殊要求尽量默认,不知道默认值为什么是0.75时我选择相信sun的工程师
如果使用HashMap做数据缓存要考虑数据量大小,如果量大则占用内存,性能也会受到影响,可以考虑会其它cache替代,oscache、ehcache或分布式缓存memcached等
MashMap非线程安全,应用ConcurrentHashMap替代
对象做key时最好注意下是否需要重写hashCode和equals,但要避免代码不合理引起的hash冲突
尽量避免遍历map,人家就不是干这个的
key为数字时使用考虑使用ArrayList,或与ArrayList配合使用寻求高效
转载:http://blog.shilimin.com/219.htm
HashMap的内部存储结构是数组,默认初始化为16长度的Entry[],对于hash冲突采用拉链方法解决,所以它是数组和链表的合体。
2、理解HashMap做到合理使用
假设我们要存放100W数据到HashMap中,那么分到每个链上就有100W/16个数据,显然这样的map是不合理的。
HashMap采用resize数组来增加数据长度降低拉链过长造成的性能影响,当put数据时put方法会判断map中的数据数量是否到达了需要扩充大小的临界点,临界点计算方法是当前数组长度*loadFactor(默认值是0.75),到了临界点后数组长度会扩容到当前数组的2倍。
resize方法相当消耗资源,不过拉链过长已经发生了性能问题,resize也只是性能分摊,以解决后面的性能问题吧,还是值得的,但当我们自己写代码时如果能够预测到HashMap中需要存放多少数据那么我们最好自己定义HashMap数组长度和加载因子loadFactor。
另外不要以为new HashMap(30)就会分配一个长度为30的数组,HashMap会重新给你算一个最优值(一个2的n次幂且大小30,n能取到最小值的数字),32,那么resize临界值就是32*0.75=24,看来指定了长度还是会resize,但构造函数里还有一个参数就是加载因子,修改new HashMap(30, 1),这样我们就指定了一个长度为32,加载因子为1的map,这时我们放30个数据时就不会触发resize了,但如果还继续增加数据,数据个数达到32时就会进行一次resize,不过这也是理所应当。
HashMap为什么要重新算一个2的n次幂长度呢?这就是HashMap中hash的精髓了
put数据过程:首先拿到数据的hashcode,然后再和数组长度-1做与运算找到应存储在数组上的位置,最后进行存储
举例:
数组长度16,存储三个数据的key的hash值分别为1、2、3
用数组长度和1、2、3分别做与操作,得到0、0、0;用数组长度-1和1、2、3做与操作得到1、2、3
用二进制表示会看的更清楚10000&1=0,10000&10=0,10000&11=0;1111&1=1,1111&10=10,1111&11=11
原因就很明显了,只要key有不同的hash值那么冲突的可能性就很平均,查询效率在整体上也会有所提前
除非定长,否则最好不要自己定义加载因子,使用默认就好(new HashMap(30)、new HashMap(64)、new HashMap(128))。
3、key值的hashCode和equals
HashMap的key可以是任意对象,假设我们用在线用户来做key,在线用户对象包括用户id,用户名,用户登录时间,那么显然这个key值中用用户id就可以唯一确定一个值,而加上用户登录时间就不一定了如果一个系统允许用户登录多次就有问题了(可以不太恰当)
这时我们就需要改写hashCode和equals方法,在计算hashCode和equals时只需要考虑用户id这一个属性就可以了
在程序书写过程中也要考虑改写对其它代码造成的影响,如果影响到其它代码了就要重新定义一个key对象了,千万不能在hashCode代码中return 1,那就悲剧了,全在一条拉链上
4、一些源码
void resize(int newCapacity) {// newCapacity为原数据长度的2倍
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);// 将老数组里的数据copy到新数组,是整个HashMap中最耗性能的代码
table = newTable;
threshold = (int)(newCapacity * loadFactor);// 重新定义临界值
}
void transfer(Entry[] newTable) {// 将老数组里的数据copy到新数组,是整个HashMap中最耗性能的代码,要遍历所有数组元素,遍历所有拉链数据,并重新计算存储点,重新拉小链cool
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
public HashMap(int initialCapacity, float loadFactor) {// 构造方法指定长度和加载因子
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)// 直到取到最优值,目的是为了减少hash冲突
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {// 计算key在数组上的存储位置,h为key的hash值,length为数组长度
return h & (length-1);
}
static class Entry<K,V> implements Map.Entry<K,V> {// 静态内部类Entry完成了LinkdList和Entry[]合体后就是HashMap的存储结构了
final K key;
V value;
Entry<K,V> next;
final int hash;
….
}
5、总结
给HashMap一个合理初始大小,注意加载因子的使用,如果没有特殊要求尽量默认,不知道默认值为什么是0.75时我选择相信sun的工程师
如果使用HashMap做数据缓存要考虑数据量大小,如果量大则占用内存,性能也会受到影响,可以考虑会其它cache替代,oscache、ehcache或分布式缓存memcached等
MashMap非线程安全,应用ConcurrentHashMap替代
对象做key时最好注意下是否需要重写hashCode和equals,但要避免代码不合理引起的hash冲突
尽量避免遍历map,人家就不是干这个的
key为数字时使用考虑使用ArrayList,或与ArrayList配合使用寻求高效
转载:http://blog.shilimin.com/219.htm
发表评论
-
生产者消费者
2018-03-26 14:58 0package com.namibank.qyl; i ... -
耦合方法重构小技巧: 内部类
2018-02-28 10:16 0打发点 -
CompletableFuture源码赏析
2018-01-29 17:26 3494文章原创,转载请注明出处:http://abc080100 ... -
Function 对代码封装带来的改变
2017-12-18 16:40 699java 8 Function特性也出现了很久了,在项目用 ... -
java 8 Stream list to Map key 重复 value合并到Collection
2017-08-09 10:38 14477关于把list转换成key value的map有很多博客上 ... -
LinkedHashMap理解
2016-11-16 14:55 996注: 下面的源码理解均基于jdk1.8的源码 ... -
泛型的使用
2016-06-22 15:08 539public class ClassTest { p ... -
关于String的问题
2015-03-05 14:07 689关于String的经典问题很多,不过弄清楚jvm如果创建和存 ... -
复习遇到的问题
2015-03-03 21:31 01 Object的clone方法没有new对象的效率高 ... -
java高并发下的唯一性验证
2014-11-21 14:03 7790做java ee程序基本上都会遇到唯一性的问题,我们通常不 ... -
java静态方法是否可以被重写
2014-11-18 17:40 2328首先来看一段代码: ... -
深入java虚拟机 异常,异常表, finally
2014-11-12 17:36 2554每个异常表入口包含 ... -
Intellij Idea + Maven 使用jstl遇到的问题
2014-11-11 10:27 3107请按照以下步骤操作: 1 在pom.xml文件中引入jst ... -
java 数组
2014-10-31 14:37 9381 数组是引用类型 2 java虚拟机在装入数组时, ... -
Amazon AWS S3 上传文件 并获取公用url
2014-06-20 13:38 52462最近在用aws的s3做云存储,把文件上传上去,在 ... -
File Path 相对路径
2014-06-07 10:32 2304最近在项目中想使用相对路径存放上传的a ... -
java Tuple 元组
2014-05-30 18:01 22042场景:当在一个方法中, 你需要返回几个对象,这几个 ... -
JodaTime 时间处理
2014-05-30 13:47 1813最近看别人在谈项目中时间处理的问题,jdk提供的Dat ... -
同步与java内存模型(转载)
2014-05-22 11:11 8331 原子性 除了long型字段和double型字段 ... -
ThreadLocal 管理 HttpSession
2014-04-24 15:52 2019最近在用spring security控制系统的权限, ...
相关推荐
在Java中,HashMap是一种广泛使用的数据结构,它基于哈希表的Map接口实现。哈希表是一种通过哈希过程将键映射到特定位置的数据结构,该位置存储了键对应的值。在详细探讨Java中HashMap的工作机制之前,首先需要理解...
在Java中,当创建一个HashMap时,其底层使用数组来存储数据。键值对存储在Entry对象中,Entry对象包含了键、值以及下一个Entry对象的引用(链表的一部分,用于解决散列冲突)。 当put一个元素时,首先会计算键的...
- **非线程安全**:由于它不是同步的,因此不能直接在多线程环境中使用,除非将其包装到`Collections.synchronizedMap()`中或使用`ConcurrentHashMap`。 - **存储null键和值**:`HashMap`允许一个`null`键和多个`...
- 线程安全:Java中的HashMap不是线程安全的,如果在多线程环境下使用,需要考虑同步机制,如使用`Collections.synchronizedMap()`或者使用`ConcurrentHashMap`。 - 空值处理:键或值为null的情况需要特殊处理,...
本资源详细介绍了 Java 中的 HashMap 类,包括其实现机制、Hash 存储机制、集合存储机制等方面的知识点。 1. HashMap 和 HashSet 的关系 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,虽然...
下面我们将详细探讨JavaScript中如何实现类似Java HashMap的功能,以及这个实现可能包含的关键知识点。 1. **哈希函数**:哈希函数是HashMap的核心,它负责将键转换为数组索引。一个优秀的哈希函数应尽量减少键的...
Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序 HashMap类Java SE程序...
Java HashMap是一种基于哈希表的数据结构,它的存储原理是通过将Key-Value对存储在一个数组中,每个数组元素是一个链表,链表中的每个元素是一个Entry对象,Entry对象包含了Key、Value和指向下一个Entry对象的引用。...
5. **线程安全性**:HashMap本身不是线程安全的,如果在多线程环境中使用,需要外部同步机制来保证数据一致性。对于线程安全的需求,可以使用ConcurrentHashMap。 HashSet是基于HashMap实现的,它不存储值,只存储...
Java语言使用hashmap实现向购物车添加删除修改商品,显示商品信息
Java 中 HashMap 类的使用详解 HashMap 是 Java 语言中最常用的集合类之一,它实现了 Map 接口,提供了 put、get、keySet 等常用方法来存储和检索数据。本文将详细介绍 HashMap 类的使用,包括其常用方法、特点和...
HashMap是Java编程语言中一种非常...总的来说,HashMap是Java中高效的键值对存储结构,但需要注意其线程安全性和哈希冲突可能导致的性能影响。在实际使用中,应根据需求选择合适的数据结构和哈希函数,以确保最佳性能。
"基于HashMap的用户标签处理兼Java中HashMap实现原理研究" 本文研究了基于HashMap的用户标签处理方法,并对Java中HashMap的实现原理进行了深入研究。HashMap是一种高效的数据结构,可以快速地存储和检索数据。本文...
在Java编程语言中,`HashMap`是`java.util`包中的一个核心类,它属于集合框架的一部分,主要用于存储键值对的数据结构。`HashMap`基于哈希表(散列表)实现,提供了快速的插入、删除和查找操作,平均时间复杂度为O(1...
本文将详细介绍如何在Java WebService中使用`HashMap`来传递和读取数据。 #### WebService与HashMap的基本概念 1. **WebService**:一种开放的标准服务,通过HTTP协议进行数据传输,可以跨平台、跨语言地提供服务...
java代码-使用java解决手写hashMap的源代码 ——学习参考资料:仅用于个人学习使用!
结合Java的HashMap中的一些优点,改进了C++ 的hash_map。 详细说明见我的博客:http://blog.csdn.net/mdj67887500/article/details/6907702
当一个类实现了Comparable接口,那么它的实例就可以进行排序,比如在集合框架中使用。 在Java 8的HashMap中,键(Key)通常是不可变的,并且它们通常需要支持排序。例如,当我们想要对HashMap的键进行排序时,可以...