貌似HashMap跟ConcurrentHashMap是面试经常考的东西,抽空来简单分析下它的源码

构造函数

  1. /**
  2. * Constructs an empty <tt>HashMap</tt> with the default initial capacity
  3. * (16) and the default load factor (0.75).
  4. */
  5. public HashMap() {
  6. this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
  7. }
  8.  
  9. /**
  10. * Constructs an empty <tt>HashMap</tt> with the specified initial
  11. * capacity and the default load factor (0.75).
  12. *
  13. * @param initialCapacity the initial capacity.
  14. * @throws IllegalArgumentException if the initial capacity is negative.
  15. */
  16. public HashMap(int initialCapacity) {
  17. this(initialCapacity, DEFAULT_LOAD_FACTOR);
  18. }
  19.  
  20. /**
  21. * Constructs an empty <tt>HashMap</tt> with the specified initial
  22. * capacity and load factor.
  23. *
  24. * @param initialCapacity the initial capacity
  25. * @param loadFactor the load factor
  26. * @throws IllegalArgumentException if the initial capacity is negative
  27. * or the load factor is nonpositive
  28. */
  29. public HashMap(int initialCapacity, float loadFactor) {
  30. if (initialCapacity < 0)
  31. throw new IllegalArgumentException("Illegal initial capacity: " +
  32. initialCapacity);
  33. if (initialCapacity > MAXIMUM_CAPACITY)
  34. initialCapacity = MAXIMUM_CAPACITY;
  35. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  36. throw new IllegalArgumentException("Illegal load factor: " +
  37. loadFactor);
  38. this.loadFactor = loadFactor;
  39. this.threshold = tableSizeFor(initialCapacity);
  40. }

第二个构造函数是调用了第三个构造函数,第三个构造函数是用用户给定的初始容量和装填因子,初始化threshold和装填因子两个变量,而threshold在代码中的描述如下:

  1. /**
  2. * The next size value at which to resize (capacity * load factor).
  3. *
  4. * @serial
  5. */
  6. // (The javadoc description is true upon serialization.
  7. // Additionally, if the table array has not been allocated, this
  8. // field holds the initial array capacity, or zero signifying
  9. // DEFAULT_INITIAL_CAPACITY.)
  10. int threshold;

从英语的字面意思上看,是指下一次map resize之后的大小,而计算的方法是通过tableSizeFor得到

  1. /**
  2. * Returns a power of two size for the given target capacity.
  3. */
  4. static final int tableSizeFor(int cap) {
  5. int n = cap - 1;
  6. n |= n >>> 1;
  7. n |= n >>> 2;
  8. n |= n >>> 4;
  9. n |= n >>> 8;
  10. n |= n >>> 16;
  11. return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
  12. }

tableSizeFor方法,是将给定的初始容量格式化成2的幂的方法,比如输入4,得到4,输入7得到8。

常用的关于HashMap的操作,主要是put(增,改),get(查),remove(删),isEmpty(查)

依次来看,首先观察下HashMap是怎么往里面(put)加入数据的:

  1. /**
  2. * Associates the specified value with the specified key in this map.
  3. * If the map previously contained a mapping for the key, the old
  4. * value is replaced.
  5. *
  6. * @param key key with which the specified value is to be associated
  7. * @param value value to be associated with the specified key
  8. * @return the previous value associated with <tt>key</tt>, or
  9. * <tt>null</tt> if there was no mapping for <tt>key</tt>.
  10. * (A <tt>null</tt> return can also indicate that the map
  11. * previously associated <tt>null</tt> with <tt>key</tt>.)
  12. */
  13. public V put(K key, V value) {
  14. return putVal(hash(key), key, value, false, true);
  15. }
  16.  
  17. /**
  18. * Implements Map.put and related methods
  19. *
  20. * @param hash hash for key
  21. * @param key the key
  22. * @param value the value to put
  23. * @param onlyIfAbsent if true, don't change existing value
  24. * @param evict if false, the table is in creation mode.
  25. * @return previous value, or null if none
  26. */
  27. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
  28. boolean evict) {
  29. Node<K,V>[] tab; Node<K,V> p; int n, i;
  30. if ((tab = table) == null || (n = tab.length) == 0)
  31. n = (tab = resize()).length;
  32. if ((p = tab[i = (n - 1) & hash]) == null)
  33. tab[i] = newNode(hash, key, value, null);
  34. else {
  35. Node<K,V> e; K k;
  36. if (p.hash == hash &&
  37. ((k = p.key) == key || (key != null && key.equals(k))))
  38. e = p;
  39. else if (p instanceof TreeNode)
  40. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  41. else {
  42. for (int binCount = 0; ; ++binCount) {
  43. if ((e = p.next) == null) {
  44. p.next = newNode(hash, key, value, null);
  45. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  46. treeifyBin(tab, hash);
  47. break;
  48. }
  49. if (e.hash == hash &&
  50. ((k = e.key) == key || (key != null && key.equals(k))))
  51. break;
  52. p = e;
  53. }
  54. }
  55. if (e != null) { // existing mapping for key
  56. V oldValue = e.value;
  57. if (!onlyIfAbsent || oldValue == null)
  58. e.value = value;
  59. afterNodeAccess(e);
  60. return oldValue;
  61. }
  62. }
  63. ++modCount;
  64. if (++size > threshold)
  65. resize();
  66. afterNodeInsertion(evict);
  67. return null;
  68. }

先来读一下put的函数说明,它是把特定的值(value)和特定的键(key)关联在map中,如果map中之前就已经包含了key,那么之前key对应的值就会被替代

返回的值表示:key之前对应的value值或者null。其中返回null值,有两种情况,1.之前在map中没有这个key存在 2.这个key值之前对应的value值就是null。

具体的代码实现,是调用了putVal方法,因此,接下来查看下putVal方法。

在看putVal方法的时候,先仔细观察下putVal方法的函数签名,前面两个是key和key对应的hash值,第三个表示要put的value,第四个是是否不更改已经存在的值,第五个是是否是

creation模式。前面三个好理解, 根据我们之前,对put方法的解读,我们可以了解到put是要干这么一件事:如果key存在,那我改value;如果key,不存在,我往里面加这个value。总之,要把给定KV 加进map里的,因此,第四个传的肯定是false,第五个是true。回过头来看put调用putVal方法给的值,就没有疑问了。

接着看putVal方法的具体实现:

putVal实现上,第一行代码就出现了Node,第一眼就蒙了,别急,接着看下Node的定义

  1. /**
  2. * Basic hash bin node, used for most entries. (See below for
  3. * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
  4. */
  5. static class Node<K,V> implements Map.Entry<K,V> {
  6. final int hash;
  7. final K key;
  8. V value;
  9. Node<K,V> next;
  10.  
  11. Node(int hash, K key, V value, Node<K,V> next) {
  12. this.hash = hash;
  13. this.key = key;
  14. this.value = value;
  15. this.next = next;
  16. }
  17.  
  18. public final K getKey() { return key; }
  19. public final V getValue() { return value; }
  20. public final String toString() { return key + "=" + value; }
  21.  
  22. public final int hashCode() {
  23. return Objects.hashCode(key) ^ Objects.hashCode(value);
  24. }
  25.  
  26. public final V setValue(V newValue) {
  27. V oldValue = value;
  28. value = newValue;
  29. return oldValue;
  30. }
  31.  
  32. public final boolean equals(Object o) {
  33. if (o == this)
  34. return true;
  35. if (o instanceof Map.Entry) {
  36. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  37. if (Objects.equals(key, e.getKey()) &&
  38. Objects.equals(value, e.getValue()))
  39. return true;
  40. }
  41. return false;
  42. }
  43. }

Node的说明,没看太懂,但它实现的代码很简单,实现了Entry接口,然后是一个链表结构,里面还存在一个next节点

现在回过头看putVal的内容,

  1. Node<K,V>[] tab; Node<K,V> p; int n, i;
  2. if ((tab = table) == null || (n = tab.length) == 0)
  3. n = (tab = resize()).length;
  4. if ((p = tab[i = (n - 1) & hash]) == null)
  5. tab[i] = newNode(hash, key, value, null);

第一行,申明这种局部变量,紧接着,将table 赋值给tab 判断是否为null,table是一个成员变量,存储Node节点,定义如下:

  1. /**
  2. * The table, initialized on first use, and resized as
  3. * necessary. When allocated, length is always a power of two.
  4. * (We also tolerate length zero in some operations to allow
  5. * bootstrapping mechanics that are currently not needed.)
  6. */
  7. transient Node<K,V>[] table;

说的是table在第一次使用的时候,初始化,resized为必需的空间,长度始终是2的幂。然后这个变量不可被序列化。第一次调用的时候table等于null,因而tab等于null,所以重新使用resize方法将值赋给tab,并将长度赋值给n.

接着来看resize方法,

  1. /**
  2. * Initializes or doubles table size. If null, allocates in
  3. * accord with initial capacity target held in field threshold.
  4. * Otherwise, because we are using power-of-two expansion, the
  5. * elements from each bin must either stay at same index, or move
  6. * with a power of two offset in the new table.
  7. *
  8. * @return the table
  9. */
  10. final Node<K,V>[] resize() {
  11. Node<K,V>[] oldTab = table;
  12. int oldCap = (oldTab == null) ? 0 : oldTab.length;
  13. int oldThr = threshold;
  14. int newCap, newThr = 0;
  15. if (oldCap > 0) {
  16. if (oldCap >= MAXIMUM_CAPACITY) {
  17. threshold = Integer.MAX_VALUE;
  18. return oldTab;
  19. }
  20. else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
  21. oldCap >= DEFAULT_INITIAL_CAPACITY)
  22. newThr = oldThr << 1; // double threshold
  23. }
  24. else if (oldThr > 0) // initial capacity was placed in threshold
  25. newCap = oldThr;
  26. else { // zero initial threshold signifies using defaults
  27. newCap = DEFAULT_INITIAL_CAPACITY;
  28. newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
  29. }
  30. if (newThr == 0) {
  31. float ft = (float)newCap * loadFactor;
  32. newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
  33. (int)ft : Integer.MAX_VALUE);
  34. }
  35. threshold = newThr;
  36. @SuppressWarnings({"rawtypes","unchecked"})
  37. Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  38. table = newTab;
  39. if (oldTab != null) {
  40. for (int j = 0; j < oldCap; ++j) {
  41. Node<K,V> e;
  42. if ((e = oldTab[j]) != null) {
  43. oldTab[j] = null;
  44. if (e.next == null)
  45. newTab[e.hash & (newCap - 1)] = e;
  46. else if (e instanceof TreeNode)
  47. ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
  48. else { // preserve order
  49. Node<K,V> loHead = null, loTail = null;
  50. Node<K,V> hiHead = null, hiTail = null;
  51. Node<K,V> next;
  52. do {
  53. next = e.next;
  54. if ((e.hash & oldCap) == 0) {
  55. if (loTail == null)
  56. loHead = e;
  57. else
  58. loTail.next = e;
  59. loTail = e;
  60. }
  61. else {
  62. if (hiTail == null)
  63. hiHead = e;
  64. else
  65. hiTail.next = e;
  66. hiTail = e;
  67. }
  68. } while ((e = next) != null);
  69. if (loTail != null) {
  70. loTail.next = null;
  71. newTab[j] = loHead;
  72. }
  73. if (hiTail != null) {
  74. hiTail.next = null;
  75. newTab[j + oldCap] = hiHead;
  76. }
  77. }
  78. }
  79. }
  80. }
  81. return newTab;
  82. }

resize方法是初始化table或者double table的大小,如果table为null,那么就用初始容量来赋值给threshold

oldCap的值等于table的长度,oldThr的值等于threshold,我们使用完构造函数初始化的时候,table是null的,因而oldCap为0;而无參构造函数没处理threshold,因而值为0,有參构造函数,将它变成赋值为一个[1,MAXIMUM_CAPACITY]的值。因而,在无參构造函数的时候——————》newCap会被赋值默认初始容量,newThr会被赋值给(默认初始容量与装填因子的乘积);在有參构造函数的时候-----》只会更改newCap赋值为threshold的值。这两步都更改了newCap的值,如果newThr没有被更改,即threshold>0,将newCap和装填因子的乘积赋给newThr,对边界判定,再重新赋值给threshold。(无參的时候,是将容量复制给threshold,现在在reSize的时候改回来了)如果当table不为空threshold也大于0的时候,边界判定,如果OK,newCap扩大一倍,newThr扩大一倍。最后,再把之前的结果,加入到新newTab中,将oldTab中的元素置为null。

OK先到这里,回到putVal方法tab resize之后,变成16个(初始容量)的Node数组,n等于初始容量。紧接着,看table[(n-1)&hash]是否为空,等于null,将其放置在这个位置上,不为空,那么就需要解决冲突,将其放入到合适的位置。里面有一个变量modCount,定义如下

  1. /**
  2. * The number of times this HashMap has been structurally modified
  3. * Structural modifications are those that change the number of mappings in
  4. * the HashMap or otherwise modify its internal structure (e.g.,
  5. * rehash). This field is used to make iterators on Collection-views of
  6. * the HashMap fail-fast. (See ConcurrentModificationException).
  7. */
  8. transient int modCount;

表示的是结构修改的次数,好像是用来检测并发修改异常?另外有一点需要注意下当一个bin(桶)装的数据太多的时候,会把Node转成TreeNode, TREEIFY_THRESHOLD = 8(常量)

  1. else if (p instanceof TreeNode)
  2. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  3. else {
  4. for (int binCount = 0; ; ++binCount) {
  5. if ((e = p.next) == null) {
  6. p.next = newNode(hash, key, value, null);
  7. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  8. treeifyBin(tab, hash);
  9. break;
    }

结合上面转成TreeNode的场景,从下面treeifyBin的代码清楚的看到,它会将对应的桶的节点转成TreeNode,先不看TreeNode的定义,从下面转化的代码,可以猜测到TreeNode除了KV属性,还包括prev(字面意思前一个),next(下一个),而且显然,他也要继承Node。

  1. /**
  2. * Replaces all linked nodes in bin at index for given hash unless
  3. * table is too small, in which case resizes instead.
  4. */
  5. final void treeifyBin(Node<K,V>[] tab, int hash) {
  6. int n, index; Node<K,V> e;
  7. if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
  8. resize();
  9. else if ((e = tab[index = (n - 1) & hash]) != null) {
  10. TreeNode<K,V> hd = null, tl = null;
  11. do {
  12. TreeNode<K,V> p = replacementTreeNode(e, null);
  13. if (tl == null)
  14. hd = p;
  15. else {
  16. p.prev = tl;
  17. tl.next = p;
  18. }
  19. tl = p;
  20. } while ((e = e.next) != null);
  21. if ((tab[index] = hd) != null)
  22. hd.treeify(tab);
  23. }
  24. }

TreeNode 简单列举下,代码太长了,就不全列了,TreeNode 应该是一个红黑树实现的

  1. static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
  2. TreeNode<K,V> parent; // red-black tree links
  3. TreeNode<K,V> left;
  4. TreeNode<K,V> right;
  5. TreeNode<K,V> prev; // needed to unlink next upon deletion
  6. boolean red;
  7. TreeNode(int hash, K key, V val, Node<K,V> next) {
  8. super(hash, key, val, next);
  9. }
  10.  
  11. /**
  12. * Returns root of tree containing this node.
  13. */
  14. final TreeNode<K,V> root() {
  15. for (TreeNode<K,V> r = this, p;;) {
  16. if ((p = r.parent) == null)
  17. return r;
  18. r = p;
  19. }
  20. }
  21.  
  22. /**
  23. * HashMap.Node subclass for normal LinkedHashMap entries.
  24. */
  25. static class Entry<K,V> extends HashMap.Node<K,V> {
  26. Entry<K,V> before, after;
  27. Entry(int hash, K key, V value, Node<K,V> next) {
  28. super(hash, key, value, next);
  29. }
  30. }

两个回调函数

afterNodeAccess,afterNodeInsertion,是空函数?这是为啥?留存

  1. // Callbacks to allow LinkedHashMap post-actions
  2. void afterNodeAccess(Node<K,V> p) { }
  3. void afterNodeInsertion(boolean evict) { }

至此,put方法实现简单的看完了,现在来看get方法

  1. /**
  2. * Returns the value to which the specified key is mapped,
  3. * or {@code null} if this map contains no mapping for the key.
  4. *
  5. * <p>More formally, if this map contains a mapping from a key
  6. * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
  7. * key.equals(k))}, then this method returns {@code v}; otherwise
  8. * it returns {@code null}. (There can be at most one such mapping.)
  9. *
  10. * <p>A return value of {@code null} does not <i>necessarily</i>
  11. * indicate that the map contains no mapping for the key; it's also
  12. * possible that the map explicitly maps the key to {@code null}.
  13. * The {@link #containsKey containsKey} operation may be used to
  14. * distinguish these two cases.
  15. *
  16. * @see #put(Object, Object)
  17. */
  18. public V get(Object key) {
  19. Node<K,V> e;
  20. return (e = getNode(hash(key), key)) == null ? null : e.value;
  21. }
  22.  
  23. /**
  24. * Implements Map.get and related methods
  25. *
  26. * @param hash hash for key
  27. * @param key the key
  28. * @return the node, or null if none
  29. */
  30. final Node<K,V> getNode(int hash, Object key) {
  31. Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
  32. if ((tab = table) != null && (n = tab.length) > 0 &&
  33. (first = tab[(n - 1) & hash]) != null) {
  34. if (first.hash == hash && // always check first node
  35. ((k = first.key) == key || (key != null && key.equals(k))))
  36. return first;
  37. if ((e = first.next) != null) {
  38. if (first instanceof TreeNode)
  39. return ((TreeNode<K,V>)first).getTreeNode(hash, key);
  40. do {
  41. if (e.hash == hash &&
  42. ((k = e.key) == key || (key != null && key.equals(k))))
  43. return e;
  44. } while ((e = e.next) != null);
  45. }
  46. }
  47. return null;
  48. }

get方法的注解挺简单的,大概意思是说返回key对应的value值,如果map中不含有key,那么返回null.注意一点地是,返回null,也有可能是key对应的值,因此,如果需要区分这两种情况,使用

containsKey方法。值得注意的一点,containsKey和get都是调用getNode方法,因此,只需要看它即可,了解二者是怎样区分的。

前面,我们在看put的时候,可以发现KV是存在Node中的,而getNode就是返回这样的Node,当存在样的Node节点的时候,getNode返回这个Key对应的节点,否则返回null。而get之所以区分不开,是因为下面这行代码

  1. return (e = getNode(hash(key), key)) == null ? null : e.value;

getNode返回的节点e 为null的时候,(表示不存在),get也返回null。当e不为null的时候,返回e.value。而e.value有可能为null。

remove操作和put操作差不多,它调用的是removeNode

  1. public V remove(Object key) {
  2. Node<K,V> e;
  3. return (e = removeNode(hash(key), key, null, false, true)) == null ?
  4. null : e.value;
  5. }
  6.  
  7. /**
  8. * Implements Map.remove and related methods
  9. *
  10. * @param hash hash for key
  11. * @param key the key
  12. * @param value the value to match if matchValue, else ignored
  13. * @param matchValue if true only remove if value is equal
  14. * @param movable if false do not move other nodes while removing
  15. * @return the node, or null if none
  16. */
  17. final Node<K,V> removeNode(int hash, Object key, Object value,
  18. boolean matchValue, boolean movable) {
  19. Node<K,V>[] tab; Node<K,V> p; int n, index;
  20. if ((tab = table) != null && (n = tab.length) > 0 &&
  21. (p = tab[index = (n - 1) & hash]) != null) {
  22. Node<K,V> node = null, e; K k; V v;
  23. if (p.hash == hash &&
  24. ((k = p.key) == key || (key != null && key.equals(k))))
  25. node = p;
  26. else if ((e = p.next) != null) {
  27. if (p instanceof TreeNode)
  28. node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
  29. else {
  30. do {
  31. if (e.hash == hash &&
  32. ((k = e.key) == key ||
  33. (key != null && key.equals(k)))) {
  34. node = e;
  35. break;
  36. }
  37. p = e;
  38. } while ((e = e.next) != null);
  39. }
  40. }
  41. if (node != null && (!matchValue || (v = node.value) == value ||
  42. (value != null && value.equals(v)))) {
  43. if (node instanceof TreeNode)
  44. ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
  45. else if (node == p)
  46. tab[index] = node.next;
  47. else
  48. p.next = node.next;
  49. ++modCount;
  50. --size;
  51. afterNodeRemoval(node);
  52. return node;
  53. }
  54. }
  55. return null;
  56. }

简单总结下:

1. HashMap 的实现方式还是传统的数组+链表的方式,但是比链表做了一个优化是,当一个桶装的元素过多的时候,他会把其转成一棵红黑树,从而做一个优化。

2. HashMap 的元素是放在table中,但table是一个transient数据,是不包括在序列化中,难道HashMap不支持序列化吗?我们从HashMap的申明,可以看到

  1. public class HashMap<K,V> extends AbstractMap<K,V>
  2. implements Map<K,V>, Cloneable, Serializable {

显然,它是可以序列化的,这样说来,他就自己重新定义了序列化的方法,我们找到了下面代码

  1. /**
  2. * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
  3. * serialize it).
  4. *
  5. * @serialData The <i>capacity</i> of the HashMap (the length of the
  6. * bucket array) is emitted (int), followed by the
  7. * <i>size</i> (an int, the number of key-value
  8. * mappings), followed by the key (Object) and value (Object)
  9. * for each key-value mapping. The key-value mappings are
  10. * emitted in no particular order.
  11. */
  12. private void writeObject(java.io.ObjectOutputStream s)
  13. throws IOException {
  14. int buckets = capacity();
  15. // Write out the threshold, loadfactor, and any hidden stuff
  16. s.defaultWriteObject();
  17. s.writeInt(buckets);
  18. s.writeInt(size);
  19. internalWriteEntries(s);
  20. }
  21.  
  22. /**
  23. * Reconstitute the {@code HashMap} instance from a stream (i.e.,
  24. * deserialize it).
  25. */
  26. private void readObject(java.io.ObjectInputStream s)
  27. throws IOException, ClassNotFoundException {
  28. // Read in the threshold (ignored), loadfactor, and any hidden stuff
  29. s.defaultReadObject();
  30. reinitialize();
  31. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  32. throw new InvalidObjectException("Illegal load factor: " +
  33. loadFactor);
  34. s.readInt(); // Read and ignore number of buckets
  35. int mappings = s.readInt(); // Read number of mappings (size)
  36. if (mappings < 0)
  37. throw new InvalidObjectException("Illegal mappings count: " +
  38. mappings);
  39. else if (mappings > 0) { // (if zero, use defaults)
  40. // Size the table using given load factor only if within
  41. // range of 0.25...4.0
  42. float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
  43. float fc = (float)mappings / lf + 1.0f;
  44. int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
  45. DEFAULT_INITIAL_CAPACITY :
  46. (fc >= MAXIMUM_CAPACITY) ?
  47. MAXIMUM_CAPACITY :
  48. tableSizeFor((int)fc));
  49. float ft = (float)cap * lf;
  50. threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
  51. (int)ft : Integer.MAX_VALUE);
  52. @SuppressWarnings({"rawtypes","unchecked"})
  53. Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
  54. table = tab;
  55.  
  56. // Read the keys and values, and put the mappings in the HashMap
  57. for (int i = 0; i < mappings; i++) {
  58. @SuppressWarnings("unchecked")
  59. K key = (K) s.readObject();
  60. @SuppressWarnings("unchecked")
  61. V value = (V) s.readObject();
  62. putVal(hash(key), key, value, false, false);
  63. }
  64. }
  65. }

Java HashMap源码分析的更多相关文章

  1. Java HashMap源码分析(含散列表、红黑树、扰动函数等重点问题分析)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  2. java HashMap源码分析(JDK8)

    这两天在复习JAVA的知识点,想更深层次的了解一下JAVA,所以就看了看JAVA的源码,把自己的分析写在这里,也当做是笔记吧,方便记忆.写的不对的地方也请大家多多指教. JDK1.6中HashMap采 ...

  3. 【JAVA集合】HashMap源码分析(转载)

    原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...

  4. Java集合源码分析(四)HashMap

    一.HashMap简介 1.1.HashMap概述 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素 ...

  5. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  6. 【Java】HashMap源码分析——基本概念

    在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...

  7. Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现

    视频通过以下四个方面介绍了HASHMAP的内容 一. 什么是HashMap Hash散列将一个任意的长度通过某种算法(Hash函数算法)转换成一个固定的值. MAP:地图 x,y 存储 总结:通过HA ...

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

    HashMap源码分析 HashMap的底层实现是面试中问到最多的,其原理也更加复杂,涉及的知识也越多,在项目中的使用也最多.因此清晰分析出其底层源码对于深刻理解其实现有重要的意义,jdk1.8之后其 ...

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

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

随机推荐

  1. 初涉JSP+JDBC 基于SQL2008的登陆验证程序

    简单的以代码的形式纪念一下,因为现在还没有解决SQL2008驱动的问题,并且有好多东西要学,所以日后会有更新~ 所安装的软件有:SQL2008,eclipse,tomcat,JDK,涉及环境配置.等等 ...

  2. Java中实现PCA降维

    package com.excellence.splitsentence; import java.net.UnknownHostException; import java.util.ArrayLi ...

  3. 1014 C语言文法定义

    <程序>→<外部声明>|<程序><外部声明><外部声明>→<函数定义>|<声明><函数定义>→<数 ...

  4. Internet History, Technology and Security (Week 5-1)

    Week 5 Technology: Internets and Packets Welcome to Week 5! This week, we'll be covering internets a ...

  5. git add -A 和 git add . 的区别

    git add -A和 git add .   git add -u在功能上看似很相近,但还是存在一点差别 git add . :他会监控工作区的状态树,使用它会把工作时的所有变化提交到暂存区,包括文 ...

  6. Linux adduser

  7. [知乎]老狼:深入PCI与PCIe之二:软件篇

    深入PCI与PCIe之二:软件篇 https://zhuanlan.zhihu.com/p/26244141 我们前一篇文章(深入PCI与PCIe之一:硬件篇 - 知乎专栏)介绍了PCI和PCIe的硬 ...

  8. Windows 10 正式版原版ISO镜像

    Win10正式版32位简体中文版(含家庭版.专业版)文件名: cn_windows_10_multiple_editions_x86_dvd_6846431.isoSHA1:21B824F402927 ...

  9. python web调用docker-py

    在 /etc/init.d/docker的start()函数末尾加入:chmod 777 /var/run/docker.sock 否则web程序会没有权限去操作  

  10. response和request的setCharacterEncoding区别

    一.request.setCharacterEncoding():是设置从request中取得的值或从数据库中取出的值. 指定后可以通过getParameter()则直接获得正确的字符串,如果不指定, ...