一、HashMap简介

1.1、HashMap概述

  HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射。此类不保证映射的顺序,假定哈希函数将元素适当的分布在各桶之间,可为基本操作(get和put)提供稳定的性能。

  在API中给出了相应的定义:

  1. //1、哈希表基于map接口的实现,这个实现提供了map所有的操作,并且提供了key和value可以为null,(HashMap和HashTable大致上是一样的除了hashmap是异步的和允许key和value为null),
  2. 这个类不确定map中元素的位置,特别要提的是,这个类也不确定元素的位置随着时间会不会保持不变。
  3. Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key.
  4. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map;
  5. in particular, it does not guarantee that the order will remain constant over time.
  6.  
  7. //假设哈希函数将元素合适的分到了每个桶(其实就是指的数组中位置上的链表)中,则这个实现为基本的操作(get、put)提供了稳定的性能,迭代这个集合视图需要的时间跟hashMap实例(key-value映射的数量)的容量(在桶中)
  8. 成正比,因此,如果迭代的性能很重要的话,就不要将初始容量设置的太高或者loadfactor设置的太低,【这里的桶,相当于在数组中每个位置上放一个桶装元素】
  9. This implementation provides constant-time performance for the basic operations (get and put), assuming the hash function disperses the elements properly among the buckets.
  10. Iteration over collection views requires time proportional to the "capacity" of the HashMap instance (the number of buckets) plus its size (the number of key-value mappings
  11. ). Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.
  12.  
  13. //HashMap的实例有两个参数影响性能,初始化容量(initialCapacity)和loadFactor加载因子,在哈希表中这个容量是桶的数量【也就是数组的长度】,一个初始化容量仅仅是在哈希表被创建时容量,在
  14. 容量自动增长之前加载因子是衡量哈希表被允许达到的多少的。当entry的数量在哈希表中超过了加载因子乘以当前的容量,那么哈希表被修改(内部的数据结构会被重新建立)所以哈希表有大约两倍的桶的数量
  15. An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table,
  16. and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before
  17. its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table
  18. is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.
  19.  
  20. //通常来讲,默认的加载因子(0.75)能够在时间和空间上提供一个好的平衡,更高的值会减少空间上的开支但是会增加查询花费的时间(体现在HashMap类中get、put方法上),当设置初始化容量时,应该考虑到map中会存放
  21. entry的数量和加载因子,以便最少次数的进行rehash操作,如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
  22.  
  23. As a general rule, the default load factor (.) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup
  24. cost (reflected in most of the operations of the HashMap class, including get and put). The expected number of entries in the map and its load factor should be taken
  25. into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of
  26. entries divided by the load factor, no rehash operations will ever occur.
  27.  
  28. //如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。
  29. If many mappings are to be stored in a HashMap instance, creating it with a sufficiently large capacity will allow the mappings to be stored more efficiently than letting
  30. it perform automatic rehashing as needed to grow the table

HashMap的定义

1.2、HashMap在JDK1.8以前数据结构和存储原理

  1)链表散列

    首先我们要知道什么是链表散列?通过数组和链表结合在一起使用,就叫做链表散列。这其实就是hashmap存储的原理图。

    

  2)HashMap的数据结构和存储原理

    HashMap的数据结构就是用的链表散列。那HashMap底层是怎么样使用这个数据结构进行数据存取的呢?分成两个部分:

    第一步:HashMap内部有一个entry的内部类,其中有四个属性,我们要存储一个值,则需要一个key和一个value,存到map中就会先将key和value保存在这个Entry类创建的对象中。

  1.      static class Entry<K,V> implements Map.Entry<K,V> {
  2.      final K key; //就是我们说的map的key
  3.      V value; //value值,这两个都不陌生
  4.      Entry<K,V> next;//指向下一个entry对象
  5.      int hash;//通过key算过来的你hashcode值。

    Entry的物理模型图:

        

    第二步:构造好了entry对象,然后将该对象放入数组中,如何存放就是这hashMap的精华所在了。

      大概的一个存放过程是:通过entry对象中的hash值来确定将该对象存放在数组中的哪个位置上,如果在这个位置上还有其他元素,则通过链表来存储这个元素。

      

  3)Hash存放元素的过程

    通过key、value封装成一个entry对象,然后通过key的值来计算该entry的hash值,通过entry的hash值和数组的长度length来计算出entry放在数组中的哪个位置上面,

    每次存放都是将entry放在第一个位置。在这个过程中,就是通过hash值来确定将该对象存放在数组中的哪个位置上。

1.3、JDK1.8后HashMap的数据结构

  

  上图很形象的展示了HashMap的数据结构(数组+链表+红黑树),桶中的结构可能是链表,也可能是红黑树,红黑树的引入是为了提高效率。

1.4、HashMap的属性

  HashMap的实例有两个参数影响其性能。

  初始容量:哈希表中桶的数量

  加载因子:哈希表在其容量自动增加之前可以达到多满的一种尺度

  当哈希表中条目数超出了当前容量*加载因子(其实就是HashMap的实际容量)时,则对该哈希表进行rehash操作,将哈希表扩充至两倍的桶数。

  Java中默认初始容量为16,加载因子为0.75。

  1. static final int DEFAULT_INITIAL_CAPACITY = << ; // aka 16
  2. static final float DEFAULT_LOAD_FACTOR = 0.75f;

  1)loadFactor加载因子

    定义:loadFactor译为装载因子。装载因子用来衡量HashMap满的程度。loadFactor的默认值为0.75f。计算HashMap的实时装载因子的方法为:size/capacity,而不是占用桶的数量去除以capacity。

    loadFactor加载因子是控制数组存放数据的疏密程度,loadFactor越趋近于1,那么数组中存放的数据(entry)也就越多,也就越密,也就是会让链表的长度增加,loadFactor越小,也就是趋近于0,

    那么数组中存放的数据也就越稀,也就是可能数组中每个位置上就放一个元素。那有人说,就把loadFactor变为1最好吗,存的数据很多,但是这样会有一个问题,就是我们在通过key拿到我们的value时,

    是先通过key的hashcode值,找到对应数组中的位置,如果该位置中有很多元素,则需要通过equals来依次比较链表中的元素,拿到我们的value值,这样花费的性能就很高,

    如果能让数组上的每个位置尽量只有一个元素最好,我们就能直接得到value值了,所以有人又会说,那把loadFactor变得很小不就好了,但是如果变得太小,在数组中的位置就会太稀,也就是分散的太开,

    浪费很多空间,这样也不好,所以在hashMap中loadFactor的初始值就是0.75,一般情况下不需要更改它。

  1. static final float DEFAULT_LOAD_FACTOR = 0.75f;

  2)桶

    根据前面画的HashMap存储的数据结构图,你这样想,数组中每一个位置上都放有一个桶,每个桶里就是装一个链表,链表中可以有很多个元素(entry),这就是桶的意思。也就相当于把元素都放在桶中。

  3)capacity

    capacity译为容量代表的数组的容量,也就是数组的长度,同时也是HashMap中桶的个数。默认值是16。

      一般第一次扩容时会扩容到64,之后好像是2倍。总之,容量都是2的幂

  1. static final int DEFAULT_INITIAL_CAPACITY = << ; // aka 16

  4)size的含义

    size就是在该HashMap的实例中实际存储的元素的个数

  5)threshold的作用

    threshold = capacity * loadFactor,当Size>=threshold的时候,那么就要考虑对数组的扩增了,也就是说,这个的意思就是衡量数组是否需要扩增的一个标准。

    注意这里说的是考虑,因为实际上要扩增数组,除了这个size>=threshold条件外,还需要另外一个条件。

    什么时候会扩增数组的大小?在put一个元素时先size>=threshold并且还要在对应数组位置上有元素,这才能扩增数组。

  1. int threshold;

  我们通过一张HashMap的数据结构图来分析:

    

二、HashMap的源码分析(一)

2.1、HashMap的层次关系与继承结构

  1)HashMap继承结构

    

  上面就继承了一个abstractMap,也就是用来减轻实现Map接口的编写负担。

  2)实现接口

    

    Map<K,V>:在AbstractMap抽象类中已经实现过的接口,这里又实现,实际上是多余的。但每个集合都有这样的错误,也没过大影响

    Cloneable:能够使用Clone()方法,在HashMap中,实现的是浅层次拷贝,即对拷贝对象的改变会影响被拷贝的对象。

    Serializable:能够使之序列化,即可以将HashMap对象保存至本地,之后可以恢复状态。

2.2、HashMap类的属性

  1. public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
  2. // 序列号
  3. private static final long serialVersionUID = 362498820763181265L;
  4. // 默认的初始容量是16
  5. static final int DEFAULT_INITIAL_CAPACITY = << ;
  6. // 最大容量
  7. static final int MAXIMUM_CAPACITY = << ;
  8. // 默认的填充因子
  9. static final float DEFAULT_LOAD_FACTOR = 0.75f;
  10. // 当桶(bucket)上的结点数大于这个值时会转成红黑树
  11. static final int TREEIFY_THRESHOLD = ;
  12. // 当桶(bucket)上的结点数小于这个值时树转链表
  13. static final int UNTREEIFY_THRESHOLD = ;
  14. // 桶中结构转化为红黑树对应的table的最小大小
  15. static final int MIN_TREEIFY_CAPACITY = ;
  16. // 存储元素的数组,总是2的幂次倍
  17. transient Node<k,v>[] table;
  18. // 存放具体元素的集
  19. transient Set<map.entry<k,v>> entrySet;
  20. // 存放元素的个数,注意这个不等于数组的长度。
  21. transient int size;
  22. // 每次扩容和更改map结构的计数器
  23. transient int modCount;
  24. // 临界值 当实际大小(容量*填充因子)超过临界值时,会进行扩容
  25. int threshold;
  26. // 填充因子
  27. final float loadFactor;
  28. }

属性

2.3、HashMap的构造方法

  有四个构造方法,构造方法的作用就是记录一下16这个数给threshold(这个数值最终会当作第一次数组的长度。)和初始化加载因子。注意,hashMap中table数组一开始就已经是个没有长度的数组了。

  构造方法中,并没有初始化数组的大小,数组在一开始就已经被创建了,构造方法只做两件事情,一个是初始化加载因子,另一个是用threshold记录下数组初始化的大小。注意是记录。

  

  1)HashMap()

  1. //看上面的注释就已经知道,DEFAULT_INITIAL_CAPACITY=16,DEFAULT_LOAD_FACTOR=0.75
  2. //初始化容量:也就是初始化数组的大小
  3. //加载因子:数组上的存放数据疏密程度。
  4. public HashMap() {
  5. this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
  6. }

  2)HashMap(int)

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

  3)HashMap(int,float)

  1. public HashMap(int initialCapacity, float loadFactor) {
  2. // 初始容量不能小于0,否则报错
  3. if (initialCapacity < )
  4. throw new IllegalArgumentException("Illegal initial capacity: " +
  5. initialCapacity);
  6. // 初始容量不能大于最大值,否则为最大值
  7. if (initialCapacity > MAXIMUM_CAPACITY)
  8. initialCapacity = MAXIMUM_CAPACITY;
  9. // 填充因子不能小于或等于0,不能为非数字
  10. if (loadFactor <= || Float.isNaN(loadFactor))
  11. throw new IllegalArgumentException("Illegal load factor: " +
  12. loadFactor);
  13. // 初始化填充因子
  14. this.loadFactor = loadFactor;
  15. // 初始化threshold大小
  16. this.threshold = tableSizeFor(initialCapacity);
  17. }

  4)HashMap(Map<? extends K, ? extends V> m)

  1. public HashMap(Map<? extends K, ? extends V> m) {
  2. // 初始化填充因子
  3. this.loadFactor = DEFAULT_LOAD_FACTOR;
  4. // 将m中的所有元素添加至HashMap中
  5. putMapEntries(m, false);
  6. } 

  putMapEntries(Map<? extends K, ? extends V> m, boolean evict)函数将m的所有元素存入本HashMap实例中

  1. final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
  2. int s = m.size();
  3. if (s > ) {
  4. // 判断table是否已经初始化
  5. if (table == null) { // pre-size
  6. // 未初始化,s为m的实际元素个数
  7. float ft = ((float)s / loadFactor) + 1.0F;
  8. int t = ((ft < (float)MAXIMUM_CAPACITY) ?
  9. (int)ft : MAXIMUM_CAPACITY);
  10. // 计算得到的t大于阈值,则初始化阈值
  11. if (t > threshold)
  12. threshold = tableSizeFor(t);
  13. }
  14. // 已初始化,并且m元素个数大于阈值,进行扩容处理
  15. else if (s > threshold)
  16. resize();
  17. // 将m中的所有元素添加至HashMap中
  18. for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
  19. K key = e.getKey();
  20. V value = e.getValue();
  21. putVal(hash(key), key, value, false, evict);
  22. }
  23. }
  24. }

三、HashMap源码分析(二)

  这里我们来看一下我们常用的一些方法的源码

3.1、put方法

  1)put(K key,V value)

  1. public V put(K key, V value) {
  2. return putVal(hash(key), key, value, false, true);
  3. }

  2)putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)

  1. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
  2. boolean evict) {
  3. Node<K,V>[] tab; Node<K,V> p; int n, i;
  4. // table未初始化或者长度为0,进行扩容
  5. if ((tab = table) == null || (n = tab.length) == )
  6. n = (tab = resize()).length;
  7. // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数组中)
  8. if ((p = tab[i = (n - ) & hash]) == null)
  9. tab[i] = newNode(hash, key, value, null);
  10. // 桶中已经存在元素
  11. else {
  12. Node<K,V> e; K k;
  13. // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等
  14. if (p.hash == hash &&
  15. ((k = p.key) == key || (key != null && key.equals(k))))
  16. // 将第一个元素赋值给e,用e来记录
  17. e = p;
  18. // hash值不相等,即key不相等;为红黑树结点
  19. else if (p instanceof TreeNode)
  20. // 放入树中
  21. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  22. // 为链表结点
  23. else {
  24. // 在链表最末插入结点
  25. for (int binCount = ; ; ++binCount) {
  26. // 到达链表的尾部
  27. if ((e = p.next) == null) {
  28. // 在尾部插入新结点
  29. p.next = newNode(hash, key, value, null);
  30. // 结点数量达到阈值,转化为红黑树
  31. if (binCount >= TREEIFY_THRESHOLD - ) // -1 for 1st
  32. treeifyBin(tab, hash);
  33. // 跳出循环
  34. break;
  35. }
  36. // 判断链表中结点的key值与插入的元素的key值是否相等
  37. if (e.hash == hash &&
  38. ((k = e.key) == key || (key != null && key.equals(k))))
  39. // 相等,跳出循环
  40. break;
  41. // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表
  42. p = e;
  43. }
  44. }
  45. // 表示在桶中找到key值、hash值与插入元素相等的结点
  46. if (e != null) {
  47. // 记录e的value
  48. V oldValue = e.value;
  49. // onlyIfAbsent为false或者旧值为null
  50. if (!onlyIfAbsent || oldValue == null)
  51. //用新值替换旧值
  52. e.value = value;
  53. // 访问后回调
  54. afterNodeAccess(e);
  55. // 返回旧值
  56. return oldValue;
  57. }
  58. }
  59. // 结构性修改
  60. ++modCount;
  61. // 实际大小大于阈值则扩容
  62. if (++size > threshold)
  63. resize();
  64. // 插入后回调
  65. afterNodeInsertion(evict);
  66. return null;
  67. } 

  HashMap并没有直接提供putVal接口给用户调用,而是提供的put函数,而put函数就是通过putVal来插入元素的。  

  3)putAlll()

3.2、get方法

  1)get(Object key)

  1. public V get(Object key) {
  2. Node<K,V> e;
  3. return (e = getNode(hash(key), key)) == null ? null : e.value;
  4. }

  2)getNode(int hash,Pbject key)

  1. final Node<K,V> getNode(int hash, Object key) {
  2. Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
  3. // table已经初始化,长度大于0,根据hash寻找table中的项也不为空
  4. if ((tab = table) != null && (n = tab.length) > &&
  5. (first = tab[(n - ) & hash]) != null) {
  6. // 桶中第一项(数组元素)相等
  7. if (first.hash == hash && // always check first node
  8. ((k = first.key) == key || (key != null && key.equals(k))))
  9. return first;
  10. // 桶中不止一个结点
  11. if ((e = first.next) != null) {
  12. // 为红黑树结点
  13. if (first instanceof TreeNode)
  14. // 在红黑树中查找
  15. return ((TreeNode<K,V>)first).getTreeNode(hash, key);
  16. // 否则,在链表中查找
  17. do {
  18. if (e.hash == hash &&
  19. ((k = e.key) == key || (key != null && key.equals(k))))
  20. return e;
  21. } while ((e = e.next) != null);
  22. }
  23. }
  24. return null;
  25. }

  HashMap并没有直接提供getNode接口给用户调用,而是提供的get函数,而get函数就是通过getNode来取得元素的。

3.3、resize方法

  1. final Node<K,V>[] resize() {
  2. // 当前table保存
  3. Node<K,V>[] oldTab = table;
  4. // 保存table大小
  5. int oldCap = (oldTab == null) ? : oldTab.length;
  6. // 保存当前阈值
  7. int oldThr = threshold;
  8. int newCap, newThr = ;
  9. // 之前table大小大于0
  10. if (oldCap > ) {
  11. // 之前table大于最大容量
  12. if (oldCap >= MAXIMUM_CAPACITY) {
  13. // 阈值为最大整形
  14. threshold = Integer.MAX_VALUE;
  15. return oldTab;
  16. }
  17. // 容量翻倍,使用左移,效率更高
  18. else if ((newCap = oldCap << ) < MAXIMUM_CAPACITY &&
  19. oldCap >= DEFAULT_INITIAL_CAPACITY)
  20. // 阈值翻倍
  21. newThr = oldThr << ; // double threshold
  22. }
  23. // 之前阈值大于0
  24. else if (oldThr > )
  25. newCap = oldThr;
  26. // oldCap = 0并且oldThr = 0,使用缺省值(如使用HashMap()构造函数,之后再插入一个元素会调用resize函数,会进入这一步)
  27. else {
  28. newCap = DEFAULT_INITIAL_CAPACITY;
  29. newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
  30. }
  31. // 新阈值为0
  32. if (newThr == ) {
  33. float ft = (float)newCap * loadFactor;
  34. newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
  35. (int)ft : Integer.MAX_VALUE);
  36. }
  37. threshold = newThr;
  38. @SuppressWarnings({"rawtypes","unchecked"})
  39. // 初始化table
  40. Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  41. table = newTab;
  42. // 之前的table已经初始化过
  43. if (oldTab != null) {
  44. // 复制元素,重新进行hash
  45. for (int j = ; j < oldCap; ++j) {
  46. Node<K,V> e;
  47. if ((e = oldTab[j]) != null) {
  48. oldTab[j] = null;
  49. if (e.next == null)
  50. newTab[e.hash & (newCap - )] = e;
  51. else if (e instanceof TreeNode)
  52. ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
  53. else { // preserve order
  54. Node<K,V> loHead = null, loTail = null;
  55. Node<K,V> hiHead = null, hiTail = null;
  56. Node<K,V> next;
  57. // 将同一桶中的元素根据(e.hash & oldCap)是否为0进行分割,分成两个不同的链表,完成rehash
  58. do {
  59. next = e.next;
  60. if ((e.hash & oldCap) == ) {
  61. if (loTail == null)
  62. loHead = e;
  63. else
  64. loTail.next = e;
  65. loTail = e;
  66. }
  67. else {
  68. if (hiTail == null)
  69. hiHead = e;
  70. else
  71. hiTail.next = e;
  72. hiTail = e;
  73. }
  74. } while ((e = next) != null);
  75. if (loTail != null) {
  76. loTail.next = null;
  77. newTab[j] = loHead;
  78. }
  79. if (hiTail != null) {
  80. hiTail.next = null;
  81. newTab[j + oldCap] = hiHead;
  82. }
  83. }
  84. }
  85. }
  86. }
  87. return newTab;
  88. }

resize()

  进行扩容,会伴随着一次重新hash分配,并且会遍历hash表中所有的元素,是非常耗时的。在编写程序中,要尽量避免resize。

  在resize前和resize后的元素布局如下:

    

    上图只是针对了数组下标为2的桶中的各个元素在扩容后的分配布局,其他各个桶中的元素布局可以以此类推。

四、总结

4.1、关于数组扩容

  从putVal源代码中我们可以知道,当插入一个元素的时候size就加1,若size大于threshold的时候,就会进行扩容。假设我们的capacity大小为32,loadFator为0.75,则threshold为24 = 32 * 0.75,

  此时,插入了25个元素,并且插入的这25个元素都在同一个桶中,桶中的数据结构为红黑树,则还有31个桶是空的,也会进行扩容处理,其实,此时,还有31个桶是空的,好像似乎不需要进行扩容处理,

  但是是需要扩容处理的,因为此时我们的capacity大小可能不适当。我们前面知道,扩容处理会遍历所有的元素,时间复杂度很高;前面我们还知道,经过一次扩容处理后,元素会更加均匀的分布在各个桶中,

  会提升访问效率。所以,说尽量避免进行扩容处理,也就意味着,遍历元素所带来的坏处大于元素在桶中均匀分布所带来的好处。 

4.2、总结

  1)要知道hashMap在JDK1.8以前是一个链表散列这样一个数据结构,而在JDK1.8以后是一个数组加链表加红黑树的数据结构。

  2)通过源码的学习,hashMap是一个能快速通过key获取到value值得一个集合,原因是内部使用的是hash查找值得方法。

参考博文:

  http://www.cnblogs.com/whgk/p/6091316.html

  http://www.cnblogs.com/leesf456/p/5242233.html

Java集合源码分析(四)HashMap的更多相关文章

  1. java集合源码分析(六):HashMap

    概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...

  2. Java 集合源码分析(一)HashMap

    目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ...

  3. java集合源码分析(三):ArrayList

    概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...

  4. Java集合源码分析(四)Vector<E>

    Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是 ...

  5. Java集合源码分析(三)LinkedList

    LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...

  6. Java集合源码分析(二)ArrayList

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

  7. java集合源码分析几篇文章

    java集合源码解析https://blog.csdn.net/ns_code/article/category/2362915

  8. Java集合源码分析(五)——HashMap

    简介 HashMap 是一个散列表,存储的内容是键值对映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口. HashM ...

  9. Java集合源码分析(七)HashMap<K, V>

    一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap  ...

随机推荐

  1. Android studio导入eclipse项目(亲测)

    之前上网搜索的时候,网上都说先用eclipse导出gradle,之后再用Android Studio的import project导入,但是这个方法使用的过程中会出现许多错误,解决了一个又一个还是不得 ...

  2. Redis4.0.0 安装及配置 (Linux — Centos7)

    本文中的两个配置文件可在这里找到 操作系统:Linux Linux发行版:Centos7 安装 下载地址,点这里Redis4.0.0.tar.gz 或者使用命令: wget http://downlo ...

  3. iOS转场动画封装

    写在前面 iOS在modal 或push等操作时有默认的转场动画,但有时候我们又需要特定的转场动画效果,从iOS7开始,苹果就提供了自定义转场的API,模态推送present和dismiss.导航控制 ...

  4. C#备份一个文件到指定的文件夹里面

    一开始我的想法是这样的: //在控制台里面操作 static void Main(string[] args) { //backup( @"D:\gg\config.xml", @ ...

  5. Keras的安装与配置

    Keras是由Python编写的基于Tensorflow或Theano的一个高层神经网络API.具有高度模块化,极简,可扩充等特性.能够实现简易和快速的原型设计,支持CNN和RNN或者两者的结合,可以 ...

  6. CSS height:100%无效

    本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/38 浏览器根本就不计算内容的高度,除非内容超出了视窗范围(导致滚 ...

  7. bzoj 4515: [Sdoi2016]游戏

    Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会 ...

  8. MySQL数据库入门(建库和建表)--陈远波

    建库.建表 1.建库 (1)SQL语句命令建库: Create database数据库名称  (该方法创建的数据库没有设置编码乱码) 1 2 3 4 5 -- 创建数据库时,设置数据库的编码方式 -- ...

  9. Python列表list对象方法总结

  10. Disruptor并发框架 (二)核心概念场景分析

    核心术语 RingBuffer(容器): 被看作Disruptor最主要的组件,然而从3.0开始RingBuffer仅仅负责存储和更新在Disruptor中流通的数据.对一些特殊的使用场景能够被用户( ...