热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

HashMap和ConcurrentHashMap的区别,HashMap的底层源码

HashMap和ConcurrentHashMap的区别:HashMap不是线程安全的,而ConcurrentHashMap是线程安全的。ConcurrentHashMap采用锁分段技术,将

HashMap和ConcurrentHashMap的区别:

  1. HashMap不是线程安全的,而ConcurrentHashMap是线程安全的。
  2. ConcurrentHashMap采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁。

  3. ConcurrentHashMap让锁的粒度更精细一些,并发性能更好。

 

HashMap的底层源码:

一. 数据结构

      Map将实际数据存储在Entry类的数组中。

transient Entry[] table;    //HashMap的成员变量,存放数据  

static class Entry implements Map.Entry { //内部类Entry
final K key;
V value;
Entry
next; //指向下一个数据
final int hash;

/**
* Creates new entry.
*/
Entry(
int h, K k, V v, Entry n) {
value
= v;
next
= n;
key
= k;
hash
= h;
}

       执行put方法时,根据key的hash值来计算放到table数组的下标,如果hash有相同的下标,则新put进去的元素放到Entry链的头部。

 

二. 属性和构造方法

属性:

1 static final int DEFAULT_INITIAL_CAPACITY = 16;  //默认的初始大小,如果执行无参的构造方法,则默认初始大小为16  
2 static final int MAXIMUM_CAPACITY = 1 <<30; //最大容量,1073741824
3 static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认的负载因子,如果没有通过构造方法传入负载因子,则使用0.75。
4 transient Entry[] table; //存放具体键值对的Entry数组
5 transient int size; //HashMap的大小
6 int threshold; //阀值 threshold = (int)(capacity * loadFactor); 即容量*负载因子,执行put方法时如果size大于threshold则进行扩容,后面put方法将会看到
7 final float loadFactor; //用户设置的负载因子
8 transient volatile int modCount; //HashMap实例被改变的次数,这个同ArrayList

 

构造方法一:

1 public HashMap() {  
2 this.loadFactor = DEFAULT_LOAD_FACTOR;
3 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
4 table = new Entry[DEFAULT_INITIAL_CAPACITY];
5 init();
6 }

使用了默认的容量和默认的负载因子。

 

构造方法二:

1 public HashMap(int initialCapacity) {  
2 this(initialCapacity, DEFAULT_LOAD_FACTOR);
3 }

使用了用户设置的初始容量和默认的负载因子。

 

构造方法三:

 1 public HashMap(int initialCapacity, float loadFactor) {  
2 if (initialCapacity <0)
3 throw new IllegalArgumentException("Illegal initial capacity: " +
4 initialCapacity);
5 if (initialCapacity > MAXIMUM_CAPACITY)
6 initialCapacity = MAXIMUM_CAPACITY;
7 if (loadFactor <= 0 || Float.isNaN(loadFactor))
8 throw new IllegalArgumentException("Illegal load factor: " +
9 loadFactor);
10
11 // Find a power of 2 >= initialCapacity
12 int capacity = 1;
13 while (capacity < initialCapacity)
14 capacity <<= 1;
15
16 this.loadFactor = loadFactor;
17 threshold = (int)(capacity * loadFactor);
18 table = new Entry[capacity];
19 init();
20 }

用户传入了初始容量和负载因子,这两个值是HashMap性能优化的关键,涉及到了HashMap的扩容问题。

HashMap的容量永远是2的倍数,如果传入的不是2的指数则被调整为大于传入值的最近的2的指数,例如如果传入130,则capacity计算后是256。是这段代码起的作用:

1 while (capacity < initialCapacity)  
2 capacity <<= 1;

 

构造方法四:

 1 public HashMap(Mapextends K, ? extends V> m) {  
2 this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
3 DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);//计算Map的大小
4 putAllForCreate(m);//初始化
5 }
6
7 private void putAllForCreate(Mapextends K, ? extends V> m) {
8 for (Iteratorextends Map.Entryextends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {//通过entryset进行遍历
9 Map.Entryextends K, ? extends V> e = i.next();
10 putForCreate(e.getKey(), e.getValue());
11 }
12 }

根据传入的map进行初始化。

 

三. 关键方法

1) put方法

 1 public V put(K key, V value) {  
2 if (key == null)
3 return putForNullKey(value); //单独处理,总是放到table[0]中
4 int hash = hash(key.hashCode()); //计算key的hash值,后面介绍性能的时候再说这个hash方法。
5 int i = indexFor(hash, table.length); //将hash和length-1取&来得到数组的下表
6 for (Entry e = table[i]; e != null; e = e.next) {
7 Object k;
8 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
9 V oldValue = e.value;
10 e.value = value;
11 e.recordAccess(this);
12 return oldValue;
13 }
14 } //如果这个key值,原来已经则替换后直接返回。
15 modCount++;
16 addEntry(hash, key, value, i);
17 return null;
18 }
19 void addEntry(int hash, K key, V value, int bucketIndex) {
20 Entry e = table[bucketIndex];
21 table[bucketIndex] = new Entry(hash, key, value, e); //如果table[bucketIndex]中已经存在Entry则放到头部。
22 if (size++ >= threshold) //如果大于了阀值,则扩容到原来大小的2倍。
23 resize(2 * table.length);
24 }
25
26 void resize(int newCapacity) {
27 Entry[] oldTable = table;
28 int oldCapacity = oldTable.length;
29 if (oldCapacity == MAXIMUM_CAPACITY) {
30 threshold = Integer.MAX_VALUE;
31 return;
32 }
33
34 Entry[] newTable = new Entry[newCapacity];
35 transfer(newTable); //赋值到新的table中,注意转移后会重新hash,所以位置可能会跟之前不同,目的是均匀分不到新的table中。
36 table = newTable;
37 threshold = (int)(newCapacity * loadFactor);
38 }

2) get方法

 1 public V get(Object key) {  
2 if (key == null)
3 return getForNullKey();
4 int hash = hash(key.hashCode());
5 for (Entry e = table[indexFor(hash, table.length)];//找到数组的下表,进行遍历
6 e != null;
7 e = e.next) {
8 Object k;
9 if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
10 return e.value;//找到则返回
11 }
12 return null;//否则,返回null
13 }

3) remove方法

 1 public V remove(Object key) {  
2 Entry e = removeEntryForKey(key);
3 return (e == null ? null : e.value);
4 }
5
6 final Entry removeEntryForKey(Object key) {
7 int hash = (key == null) ? 0 : hash(key.hashCode());
8 int i = indexFor(hash, table.length);
9 Entry prev = table[i];
10 Entry e = prev;
11
12 while (e != null) {//Entry链未遍历完则一直遍历
13 Entry next = e.next;
14 Object k;
15 if (e.hash == hash &&
16 ((k = e.key) == key || (key != null && key.equals(k)))) {
17 modCount++;
18 size--;
19 if (prev == e)//如果是第一个,则将table[i]执行e.next
20 table[i] = next;
21 else //否则将前一个的next指向e.next
22 prev.next = next;
23 e.recordRemoval(this);
24 return e;
25 }
26 prev = e;//未找到则继续往后遍历
27 e = next;
28 }
29
30 return e;
31 }

4) HashMap的遍历方法

 1 Map map = new HashMap();  
2 map.put("key1","value1");
3 map.put("key2","value2");
4 map.put("key3", "value3");
5 for(Iterator it = map.entrySet().iterator();it.hasNext();){
6 Map.Entry e = (Map.Entry)it.next();
7 System.out.println(e.getKey() + "=" + e.getValue());
8 }
9 System.out.println("-----------------------------------------");
10 for(Iterator it = map.keySet().iterator();it.hasNext();){
11 Object key = it.next();
12 Object value = map.get(key);
13 System.out.println(key+"="+value);
14 }
15 System.out.println("-----------------------------------------");
16 System.out.println(map.values());
17
18 输出为:
19 key3=value3
20 key2=value2
21 key1=value1
22 -----------------------------------------
23 key3=value3
24 key2=value2
25 key1=value1
26 -----------------------------------------
27 [value3, value2, value1]

 

                                                      原文地址: http://frank1234.iteye.com/blog/2162490

 


推荐阅读
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 线程能否先以安全方式获取对象,再进行非安全发布? ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • 本文深入解析了WCF Binding模型中的绑定元素,详细介绍了信道、信道管理器、信道监听器和信道工厂的概念与作用。从对象创建的角度来看,信道管理器负责信道的生成。具体而言,客户端的信道通过信道工厂进行实例化,而服务端则通过信道监听器来接收请求。文章还探讨了这些组件之间的交互机制及其在WCF通信中的重要性。 ... [详细]
  • ### 优化后的摘要本学习指南旨在帮助读者全面掌握 Bootstrap 前端框架的核心知识点与实战技巧。内容涵盖基础入门、核心功能和高级应用。第一章通过一个简单的“Hello World”示例,介绍 Bootstrap 的基本用法和快速上手方法。第二章深入探讨 Bootstrap 与 JSP 集成的细节,揭示两者结合的优势和应用场景。第三章则进一步讲解 Bootstrap 的高级特性,如响应式设计和组件定制,为开发者提供全方位的技术支持。 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 深入解析CAS机制:全面替代传统锁的底层原理与应用
    本文深入探讨了CAS(Compare-and-Swap)机制,分析了其作为传统锁的替代方案在并发控制中的优势与原理。CAS通过原子操作确保数据的一致性,避免了传统锁带来的性能瓶颈和死锁问题。文章详细解析了CAS的工作机制,并结合实际应用场景,展示了其在高并发环境下的高效性和可靠性。 ... [详细]
  • 本文详细介绍了在 Oracle 数据库中使用 MyBatis 实现增删改查操作的方法。针对查询操作,文章解释了如何通过创建字段映射来处理数据库字段风格与 Java 对象之间的差异,确保查询结果能够正确映射到持久层对象。此外,还探讨了插入、更新和删除操作的具体实现及其最佳实践,帮助开发者高效地管理和操作 Oracle 数据库中的数据。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本文将继续探讨 JavaScript 函数式编程的高级技巧及其实际应用。通过一个具体的寻路算法示例,我们将深入分析如何利用函数式编程的思想解决复杂问题。示例中,节点之间的连线代表路径,连线上的数字表示两点间的距离。我们将详细讲解如何通过递归和高阶函数等技术实现高效的寻路算法。 ... [详细]
  • 在多年使用Java 8进行新应用开发和现有应用迁移的过程中,我总结了一些非常实用的技术技巧。虽然我不赞同“最佳实践”这一术语,因为它可能暗示了通用的解决方案,但这些技巧在实际项目中确实能够显著提升开发效率和代码质量。本文将深入解析并探讨这四大高级技巧的具体应用,帮助开发者更好地利用Java 8的强大功能。 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
author-avatar
huo斌_340
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有