概览:

内部存储的数据结构为:数组+链表+红黑树,图示:

重要的属性(内部类):

  1. //存放元素的数组
  2. transient volatile Node<K,V>[] table;
  3. //数组中的Node节点
  4. static class Node<K,V> implements Map.Entry<K,V> {
  5. final int hash;//Key计算出来的Hash值
  6. final K key;//Key
  7. volatile V val;//Value
  8. volatile Node<K,V> next;//链表的下一个节点
  9.  
  10. Node(int hash, K key, V val, Node<K,V> next) {
  11. this.hash = hash;
  12. this.key = key;
  13. this.val = val;
  14. this.next = next;
  15. }
  16. ...//省略
  17. }
  18. //红黑树中的节点
  19. static final class TreeNode<K,V> extends Node<K,V> {
  20. TreeNode<K,V> parent; // red-black tree links
  21. TreeNode<K,V> left; //左子节点
  22. TreeNode<K,V> right; //右子节点
  23. TreeNode<K,V> prev; //
  24. boolean red;
  25. ...//省略
  26. }
  27.  
  28. //组合TreeNode
  29. static final class TreeBin<K,V> extends Node<K,V> {
  30. TreeNode<K,V> root;
  31. volatile TreeNode<K,V> first;
  32. }
  33.  
  34. //内部类

方法分析

  1. /** Implementation for put and putIfAbsent */
  2. final V putVal(K key, V value, boolean onlyIfAbsent) {
  3. if (key == null || value == null) throw new NullPointerException();
  4. int hash = spread(key.hashCode());
  5. int binCount = 0;
  6. for (Node<K,V>[] tab = table;;) {
  7. Node<K,V> f; int n, i, fh;
  8. if (tab == null || (n = tab.length) == 0)
  9. tab = initTable();//初始化数组大小,默认16
  10. //数组指定位置元素为空,直接插入
  11. else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
  12. if (casTabAt(tab, i, null,
  13. new Node<K,V>(hash, key, value, null)))
  14. break; // no lock when adding to empty bin
  15. }
  16. else if ((fh = f.hash) == MOVED)
  17. tab = helpTransfer(tab, f);
  18. else {//不为空,链表存储
  19. V oldVal = null;
  20. synchronized (f) {
  21. if (tabAt(tab, i) == f) {
  22. if (fh >= 0) {
  23. binCount = 1;
  24. for (Node<K,V> e = f;; ++binCount) {
  25. K ek;
  26. if (e.hash == hash &&
  27. ((ek = e.key) == key ||
  28. (ek != null && key.equals(ek)))) {
  29. oldVal = e.val;
  30. if (!onlyIfAbsent)
  31. e.val = value;
  32. break;
  33. }
  34. Node<K,V> pred = e;
  35. if ((e = e.next) == null) {
  36. pred.next = new Node<K,V>(hash, key,
  37. value, null);
  38. break;
  39. }
  40. }
  41. }
  42. //红黑树
  43. else if (f instanceof TreeBin) {
  44. Node<K,V> p;
  45. binCount = 2;
  46. if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
  47. value)) != null) {
  48. oldVal = p.val;
  49. if (!onlyIfAbsent)
  50. p.val = value;
  51. }
  52. }
  53. }
  54. }
  55. if (binCount != 0) {
  56. //链表长度大于8,转为红黑树存储
  57. if (binCount >= TREEIFY_THRESHOLD)
  58. treeifyBin(tab, i);
  59. if (oldVal != null)
  60. return oldVal;
  61. break;
  62. }
  63. }
  64. }
  65. addCount(1L, binCount);
  66. return null;
  67. }
  1. /**
  2. * Replaces all linked nodes in bin at given index unless table is
  3. * too small, in which case resizes instead.
  4. */
  5. private final void treeifyBin(Node<K,V>[] tab, int index) {
  6. Node<K,V> b; int n, sc;
  7. if (tab != null) {
  8. if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
  9. tryPresize(n << 1);//扩容数组
  10. else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
  11. synchronized (b) {
  12. if (tabAt(tab, index) == b) {
  13. TreeNode<K,V> hd = null, tl = null;
                  //遍历链表,构造TreeNode
  14. for (Node<K,V> e = b; e != null; e = e.next) {
  15. TreeNode<K,V> p =
  16. new TreeNode<K,V>(e.hash, e.key, e.val,
  17. null, null);
  18. if ((p.prev = tl) == null)
  19. hd = p;
  20. else
  21. tl.next = p;
  22. tl = p;
  23. }
    //构建红黑树
  24. setTabAt(tab, index, new TreeBin<K,V>(hd));
  25. }
  26. }
  27. }
  28. }
  29. }
  1. public V get(Object key) {
  2. Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
  3. int h = spread(key.hashCode());
  4. if ((tab = table) != null && (n = tab.length) > 0 &&
  5. (e = tabAt(tab, (n - 1) & h)) != null) {
  6. if ((eh = e.hash) == h) {
  7. if ((ek = e.key) == key || (ek != null && key.equals(ek)))
  8. return e.val;
  9. }
  10. else if (eh < 0)//红黑树取值
  11. return (p = e.find(h, key)) != null ? p.val : null;
    //链表取值
  12. while ((e = e.next) != null) {
  13. if (e.hash == h &&
  14. ((ek = e.key) == key || (ek != null && key.equals(ek))))
  15. return e.val;
  16. }
  17. }
  18. return null;
  19. }

写在最后:

为什么使用红黑树?

红黑树的特性:

1、节点是红色或者黑色

2、根是黑色

3、所有叶子都是黑色

4、每个红色节点必须有2个黑色的子节点

5、从任一节点到其每个叶子的所有简单路径包含相同数目的黑色节点

  根据特性5,从根的最长路径不可能>2倍的最短路径,所以这样的二叉树是平衡的;插入、删除、查询操作比较高效

拓展阅读

    1、红黑树介绍    2、深入分析ConcurrentHashMap1.8的扩容实现

ConcurrentHashMap笔记的更多相关文章

  1. Java基础知识强化之集合框架笔记76:ConcurrentHashMap之 ConcurrentHashMap简介

    1. ConcurrentHashMap简介: ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和Hashtable功能相同但是线程安全的方法.Conc ...

  2. java并发编程笔记(十)——HashMap与ConcurrentHashMap

    java并发编程笔记(十)--HashMap与ConcurrentHashMap HashMap参数 有两个参数影响他的性能 初始容量(默认为16) 加载因子(默认是0.75) HashMap寻址方式 ...

  3. Java并发编程笔记之ConcurrentHashMap原理探究

    在多线程环境下,使用HashMap进行put操作时存在丢失数据的情况,为了避免这种bug的隐患,强烈建议使用ConcurrentHashMap代替HashMap. HashTable是一个线程安全的类 ...

  4. java Concurrent包学习笔记(七):ConcurrentHashMap

    (注意:以下讲解的ConcurrentHashMap是jdk 1.8的) 一.ConcurrentHashMap的数据结构 ConcurrentHashMap在1.8中的实现,相比于1.7的版本基本上 ...

  5. Java基础知识强化之集合框架笔记78:ConcurrentHashMap之 ConcurrentHashMap、Hashtable、HashMap、TreeMap区别

    1. Hashtable: (1)是一个包含单向链的二维数组,table数组中是Entry<K,V>存储,entry对象: (2)放入的value不能为空: (3)线程安全的,所有方法均用 ...

  6. Java基础知识强化之集合框架笔记77:ConcurrentHashMap之 ConcurrentHashMap的基本操作

    1. ConcurrentHashMap的初始化: 下面我们来结合源代码来具体分析一下ConcurrentHashMap的实现,先看下初始化方法: public ConcurrentHashMap(i ...

  7. JDK源码阅读(7):ConcurrentHashMap类阅读笔记

    ConcurrentHashMap public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implement ...

  8. Java性能调优笔记

    Java性能调优笔记 调优步骤:衡量系统现状.设定调优目标.寻找性能瓶颈.性能调优.衡量是否到达目标(如果未到达目标,需重新寻找性能瓶颈).性能调优结束. 寻找性能瓶颈 性能瓶颈的表象:资源消耗过多. ...

  9. effective java 第2章-创建和销毁对象 读书笔记

    背景 去年就把这本javaer必读书--effective java中文版第二版 读完了,第一遍感觉比较肤浅,今年打算开始第二遍,顺便做一下笔记,后续会持续更新. 1.考虑用静态工厂方法替代构造器 优 ...

随机推荐

  1. 怎样处理Gradle中的这个文件下载慢的问题的

    如图:在build.gradle中的dependencies中加上要依赖的包后,就点击sync gradle.然后就开始了下载.在此过程中我是FQ了的(在此同时我是可以用chrome进入https:/ ...

  2. Android实战技巧之八:Ubuntu下切换JDK版本【转】

    本文转载自:http://blog.csdn.net/lincyang/article/details/42024565 Android L之后推荐使用JDK7编译程序,这是自然发展规律,就像是4年前 ...

  3. docker 清理容器和镜像

    在docker运行过程中,会不知不觉造出很多容器,很多都是不用的,需要清理. 下面就是一些清理办法,一个个清理肯定很低效,批量清理很有意思. 查看正在运行的容器 # docker ps -q 9b9f ...

  4. javaSE基础(二)

    文件:文件是信息在计算机上的保存形式. 可控式异常:一种必须被处理或必须在可能产生异常的方法中给出声明的异常. 可控式异常的三种处理方式: 1)try...catch捕获 2)throws语句往上抛 ...

  5. 蓝书4.1-4.4 树状数组、RMQ问题、线段树、倍增求LCA

    这章的数据结构题很真实 T1 排队 bzoj 1699 题目大意: 求静态一些区间的最大值-最小值 思路: ST表裸题 #include<iostream> #include<cst ...

  6. bzoj 1370 Gang团伙

    题目大意: 在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,满足 1. 我朋友的朋友是我的朋友 2. 我敌人的敌人是我的朋友 所有是朋友的人组成一个团伙 告诉你关于这n个人的m条信息,即某两个 ...

  7. Java 中extends与implements使用方法 (转载)

    转自:http://blog.csdn.net/chen_chun_guang/article/details/6323201 初学Java语言, 代码中的extends和implements让我感到 ...

  8. Linux学习之01_基础命令介绍

    初学Linux,还在摸索中,在这个过程中希望能记录下学习到的东西,参考的的书籍为<鸟哥的Linux私房菜> 在这里学到的主要命令有这几个: data cal bc man shutdown ...

  9. datatable-bootstrap 基本配置

    function doSearch() { if(dtable!=null){ dtable.fnClearTable(0); dtable.fnDraw(); // 重新加载数据 }else{ dt ...

  10. 01背包(类) UVA 10564 Paths through the Hourglass

    题目传送门 /* 01背包(类):dp[i][j][k] 表示从(i, j)出发的和为k的方案数,那么cnt = sum (dp[1][i][s]) 状态转移方程:dp[i][j][k] = dp[i ...