HashMap源码分析(Java8)
1. HashMap
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认初始容量:16
static final int MAXIMUM_CAPACITY = 1 << 30; // 最大容量:2^30
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认加载因子:0.75
static final int TREEIFY_THRESHOLD = 8; // 链表转红黑树的阈值:8
static final int UNTREEIFY_THRESHOLD = 6; // 红黑树转链表的阈值:6
static final int MIN_TREEIFY_CAPACITY = 64; // 链表转红黑树的最小容量:64
// 若容量小于64,则binCount大于8时,不将链表转为红黑树(treeify),而是扩容(resize) transient Node<K,V>[] table; // 哈希表(HashMap.Node implements Map.Entry)
transient Set<Map.Entry<K,V>> entrySet; // Entry集合(HashMap.EntrySet)
transient int size; // table中Node元素个数
transient int modCount; // HashMap修改计数,调用put、remove、clear...:modCount++(与HashIterator相关)
int threshold; // table已分配空间时,threshold为table下次扩容阈值
// table未分配空间时,threshold可用于保存指定的初始容量
final float loadFactor; // 加载因子 public HashMap(int initialCapacity, float loadFactor) {
... ...
this.loadFactor = loadFactor;
// table容量必须为2^n(建议看下tableSizeFor方法)
this.threshold = tableSizeFor(initialCapacity);
} public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
} public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
} public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false); // 用putVal方法将m中的元素添加到table中
}
... ...
}
1)get
1‘ 根据key找到映射的bin
2’ 若bin的首个节点是否为待查找节点,则直接返回
3' 若bin为红黑树则在红黑树中查找,若bin为链表则遍历链表
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
} final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
// 已经为table分配空间 && key映射的bin存在
if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
// 判断bin中首个节点是否为待查找节点
if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode) // bin为红黑树根节点,则在红黑树中查找
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do { // bin为链表,则遍历链表
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
2)put
1‘ 根据key找到映射的bin
2’ 若bin是否为空,则直接添加节点
3' 若bin为红黑树则在红黑树中添加节点,若bin为链表则在链表尾部添加节点
4' 若bin中已存在具有相同key的节点,则覆盖(或不覆盖)原value
5' 若bin中不存在具有相同key的节点,则size++并根据size判断是否扩容
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
} final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0) // table尚未分配空间
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null) // 当前bin为空
tab[i] = newNode(hash, key, value, null);
else { // 当前bin不为空
Node<K,V> e; K k;
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // 判断首个节点中存在相同的key
e = p;
else if (p instanceof TreeNode) // bin为红黑树,则在红黑数中添加节点
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { // bin为链表,则在链表中插入节点
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null); // 在链表尾部添加节点
if (binCount >= TREEIFY_THRESHOLD - 1) // 链表长度 >= 8,链表转为红黑树
treeifyBin(tab, hash);
break;
}
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) // bin中存在具有相同key的节点
break;
p = e;
}
}
if (e != null) { // bin中存在具有相同key的节点
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null) // 判断是否覆盖旧值
e.value = value;
afterNodeAccess(e); // noop
return oldValue;
}
}
++modCount;
if (++size > threshold) // 添加元素后,超过扩容阈值
resize(); // 扩容
afterNodeInsertion(evict); // noop
return null;
}
3)remove
1‘ 根据key找到映射的bin
2’ 判断bin的首个节点是否为待删除节点
3' 若bin为红黑树则在红黑树中查找,若bin为链表则遍历链表
4' 若找到相应的节点,则在红黑树或链表中删除之
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
} final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
// 已经为table分配空间 && key映射的bin存在
if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // 判断首个节点是否为待删除节点
node = p;
else if ((e = p.next) != null) {
if (p instanceof TreeNode) // bin为空黑树,则在红黑树中查找
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else { // bin为链表,则遍历链表
do {
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
// 在bin中找到被删除节点node && (删除node时不比较value || 删除node时比较value,而value相等)
if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) {
if (node instanceof TreeNode) // 在红黑树中删除节点
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p) // 待删除节点为链表首个节点
tab[index] = node.next;
else // 待删除节点非链表首个节点
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node); // noop
return node;
}
}
return null;
}
4)resize
调用putVal方法往table中添加元素时:
1' 若table尚未分配空间,则调用此方法进行初始化
2' 在table中添加元素后,若size大于扩容阈值,则调用此方法进行扩容
3‘ 迁移旧table中的节点到新table中
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) { // 已为table分配空间,则扩容
if (oldCap >= MAXIMUM_CAPACITY) { // table容量 >= 2^30,扩容阈值设为最大整数,不扩容
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 1. 新table容量 = 旧table容量 * 2
// 2. 若新table容量 < 2^30,且旧table容量 >= 16,则新table阈值 = 旧table阈值 << 1
// 3. 否则,新table阈值另行计算(新table阈值 = 最大整数 / 新table容量 * 加载因子)
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // 新的阈值 = 旧的阈值 << 1
}
else if (oldThr > 0) // table尚未分配空间,指定初始容量(保存在thredshold中)
newCap = oldThr;
else { // table尚未分配空间,默认初始容量(16)
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) { // 另行计算新table阈值,最大整数 / 新容量 * 加载因子
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) { // 当前正在扩容(非初始化)
for (int j = 0; j < oldCap; ++j) { // 遍历旧table,迁移节点到新table中
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode) // 当前bin为红黑树
((TreeNode<K,V>)e).split(this, newTab, j, oldCap); // 分割红黑树到新table中
else { // 当前bin为链表
Node<K,V> loHead = null, loTail = null; // 低位链表
Node<K,V> hiHead = null, hiTail = null; // 高位链表
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) { // e进入低位链表
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else { // e进入高位链表
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead; // 低位链表挂至新table
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead; // 高位链表挂至新table
}
}
}
}
}
return newTab;
}
5)treeifyBin
调用putVal方法往table中添加元素时,新元素所在bin中元素数量 > 8时,调用此方法将bin由单向链表转化为双向链表 + 红黑树。
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
// table未初始化 || table容量 < 64,则进行resize(table初始化 || table扩容)
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) { // 在当前bin上建立双向链表,并在双向链表上建立红黑树
TreeNode<K,V> hd = null, tl = null; // 双向链表头、尾节点
do {
TreeNode<K,V> p = replacementTreeNode(e, null); // Node -> TreeNode
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab); // 在双向链表上继续建立红黑树
}
}
6)containsKey和containsValue
containsKey:getNode返回非空
containsValue:依次遍历bin,并依次遍历bin中链表
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
} public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
if ((tab = table) != null && size > 0) {
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next) { // 在bin中遍历链表
if ((v = e.value) == value || (value != null && value.equals(v)))
return true;
}
}
}
return false;
}
7)entrySet、keySet和values
依次返回EntrySet、KeySet、Values
1' EntrySet、KeySet继承自AbstractSet,而Values继承自AbstractCollection
2' EntrySet、KeySet、Values对应的迭代器分别为:EntryIterator、KeyIterator、ValueIterator
3' EntryIterator、KeyIterator、ValueIterator均继承自HashIterator,依赖HashIterator.nextNode分别对Node(Entry)、K、V进行迭代
4' 对EntrySet、KeySet、Values,及各自迭代器,调用remove方法,都将最终调用HashMap的removeNode方法删除节点
5' 对EntrySet、KeySet、Values,不能调用add方法添加元素,否则将抛出UnsupportedOperationException
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
} public Set<K> keySet() {
Set<K> ks;
return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
} public Collection<V> values() {
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; } // return HashMap.this.size
public final void clear() { HashMap.this.clear(); } // HashMap.this.clear
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator(); // HashMap.EntryIterator
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key); // HashMap.this.getNode
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null; // HashMap.this.removeNode
}
return false;
}
// add方法继承自AbstractCollection:throw UnsupportedOperationException
... ...
} final class KeySet extends AbstractSet<K> {
public final int size() { return size; } // return HashMap.this.size
public final void clear() { HashMap.this.clear(); } // HashMap.this.clear
public final Iterator<K> iterator() { return new KeyIterator(); } // HashMap.KeyIterator
public final boolean contains(Object o) { return containsKey(o); } // HashMap.this.containsKey
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null; // HashMap.this.removeNode,matchValue为true
}
// add方法继承自AbstractCollection:throw UnsupportedOperationException
... ...
} final class Values extends AbstractCollection<V> {
public final int size() { return size; } // return HashMap.this.size
public final void clear() { HashMap.this.clear(); } // HashMap.this.clear
public final Iterator<V> iterator() { return new ValueIterator(); } // HashMap.ValueIterator
public final boolean contains(Object o) { return containsValue(o); } // HashMap.this.containsValue
// remove方法继承自AbstractCollection(用ValueIterator查找和删除首个匹配的节点)
// add方法继承自AbstractCollection:throw UnsupportedOperationException
... ...
}
abstract class HashIterator {
Node<K,V> next; // 下一节点
Node<K,V> current; // 当前节点
int expectedModCount;
int index; // 下一bin位置 HashIterator() {
expectedModCount = modCount; // 纪录当前HashMap的修改计数
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) {
// 在table中找到第一处非空bin,next指向该bin首个节点,index指向下个bin
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
// 若当前bin中,next后置位还有节点,则next指向后置节点
// 若当前bin中,next后置位没有节点,则next指向后置bin中首个节点(如果存在)
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false); // HashMap.this.removeNode
expectedModCount = modCount;
}
} final class KeyIterator extends HashIterator implements Iterator<K> {
public final K next() { return nextNode().key; }
} final class ValueIterator extends HashIterator implements Iterator<V> {
public final V next() { return nextNode().value; }
} final class EntryIterator extends HashIterator implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
2. HashMap.TreeNode(红黑树)
// 红黑树更详细实现,请大家参考java.util.TreeMap,本文只针对HashMap中引入的红黑树作简要的描述
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // 父节点
TreeNode<K,V> left; // 左孩子
TreeNode<K,V> right; // 右孩子
TreeNode<K,V> prev; // 前置节点(next继承自HashMap.Node)
boolean red; // 红黑标记 TreeNode(int hash, K key, V val, Node<K,V> next);
// 红黑树根节点
final TreeNode<K,V> root();
// 当前红黑树也是一个双向链表,该方法将红黑树root移至双向链表头部
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
int n;
if (root != null && tab != null && (n = tab.length) > 0) {
int index = (n - 1) & root.hash;
TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
if (root != first) { // root不是双向链表头节点
Node<K,V> rn;
tab[index] = root;
TreeNode<K,V> rp = root.prev;
if ((rn = root.next) != null) // root存在后置节点
((TreeNode<K,V>)rn).prev = rp;
if (rp != null) // root存在前置节点
rp.next = rn;
if (first != null)
first.prev = root;
root.next = first;
root.prev = null;
}
assert checkInvariants(root);
}
} // 从当前节点开始查找
final TreeNode<K,V> find(int h, Object k, Class<?> kc);
// 从root开始查找
final TreeNode<K,V> getTreeNode(int h, Object k); // key所属类未实现Comparable接口,且可能覆盖Object.hashCode方法
// 用System.identityHashCode(key)比较TreeNode大小
static int tieBreakOrder(Object a, Object b); // 在双向链表上建立红黑树
final void treeify(Node<K,V>[] tab) {
TreeNode<K,V> root = null;
// 当前节点为双向链表头结点
for (TreeNode<K,V> x = this, next; x != null; x = next) { // 遍历双向链表,x为当前节点
next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
if (root == null) { // 红黑树为空
x.parent = null;
x.red = false;
root = x;
}
else { // 红黑树不为空
K k = x.key;
int h = x.hash;
Class<?> kc = null;
for (TreeNode<K,V> p = root;;) {
int dir, ph;
K pk = p.key;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) || // K未实现Comparable接口
(dir = compareComparables(kc, k, pk)) == 0) // compareTo return 0
dir = tieBreakOrder(k, pk);// 用System.identityHashCode(key)比较TreeNode大小
TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) { // 往左走,左子树为空 || 往右走,右子树为空
x.parent = xp; // x父节点置为xp
if (dir <= 0)
xp.left = x; // x插入xp左子树
else
xp.right = x; // x插入xp右子树
root = balanceInsertion(root, x);
break;
}
}
}
}
moveRootToFront(tab, root);
} // 红黑树转单向链表
final Node<K,V> untreeify(HashMap<K,V> map) {
Node<K,V> hd = null, tl = null; // 链表头、尾节点
for (Node<K,V> q = this; q != null; q = q.next) {
Node<K,V> p = map.replacementNode(q, null); // TreeNode -> Node
if (tl == null)
hd = p;
else
tl.next = p;
tl = p;
}
return hd;
} // 在红黑树中插入节点(balanceInsertion)
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v); // 在红黑树中删除当前节点(balanceDeletion)
final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab, boolean movable); // 分割红黑树(table扩容)
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
TreeNode<K,V> b = this;
TreeNode<K,V> loHead = null, loTail = null; // 低位双向链表
TreeNode<K,V> hiHead = null, hiTail = null; // 高位双向链表
int lc = 0, hc = 0;
// 当前节点为双向链表头结点
for (TreeNode<K,V> e = b, next; e != null; e = next) { // 遍历双向链表,e为当前节点
next = (TreeNode<K,V>)e.next;
e.next = null;
if ((e.hash & bit) == 0) { // e进入低位双向链表
if ((e.prev = loTail) == null)
loHead = e;
else
loTail.next = e;
loTail = e;
++lc; // 低位节点计数++
}
else { // e进入高位双向链表
if ((e.prev = hiTail) == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
++hc; // 高位节点计数++
}
} if (loHead != null) {
if (lc <= UNTREEIFY_THRESHOLD) // 低位链表大小 <= 6
tab[index] = loHead.untreeify(map); // 用低位双向链表建立单向链表
else {
tab[index] = loHead;
if (hiHead != null) // 节点没有全部进入低位双向链表
loHead.treeify(tab); // 在低位双向链表上建立红黑树
}
}
if (hiHead != null) {
if (hc <= UNTREEIFY_THRESHOLD) // 高位节点数 <= 6
tab[index + bit] = hiHead.untreeify(map); // 用高位双向链表建立单向链表
else {
tab[index + bit] = hiHead;
if (loHead != null) // 节点没有全部进入高位双向链表
hiHead.treeify(tab); // 在高位双向链表上建立红黑树
}
}
} // 以p为支点左旋
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root, TreeNode<KeNode<K,V> p); // 以p为支点右旋
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<KeNode<K,V> p); // 插入节点后平衡调整
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x); // 删除节点后平衡调整
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root, TreeNode<K,V> x); ... ...
}
HashMap源码分析(Java8)的更多相关文章
- 【JAVA集合】HashMap源码分析(转载)
原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...
- Java中HashMap源码分析
一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtab ...
- JDK1.8 HashMap源码分析
一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...
- HashMap源码分析和应用实例的介绍
1.HashMap介绍 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射.HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.S ...
- 【Java】HashMap源码分析——常用方法详解
上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...
- 【Java】HashMap源码分析——基本概念
在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...
- Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现
视频通过以下四个方面介绍了HASHMAP的内容 一. 什么是HashMap Hash散列将一个任意的长度通过某种算法(Hash函数算法)转换成一个固定的值. MAP:地图 x,y 存储 总结:通过HA ...
- Java源码解析——集合框架(五)——HashMap源码分析
HashMap源码分析 HashMap的底层实现是面试中问到最多的,其原理也更加复杂,涉及的知识也越多,在项目中的使用也最多.因此清晰分析出其底层源码对于深刻理解其实现有重要的意义,jdk1.8之后其 ...
- HashMap源码分析(史上最详细的源码分析)
HashMap简介 HashMap是开发中使用频率最高的用于映射(键值对 key value)处理的数据结构,我们经常把hashMap数据结构叫做散列链表: ObjectI entry<Key, ...
- HashMap 源码分析 基于jdk1.8分析
HashMap 源码分析 基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table; //这里维护了一个 Node的数组结构: 下面看看Node的数 ...
随机推荐
- Install Rancher server
1.pre-requirement: sudo nmtui # sudo hostnamectl set-hostname <hostname> $ sudo hostnamectl se ...
- CodeSmith和PowerDesigner (转)
首先,既然要讲解如何使用CodeSmith和PowerDesigner快速生成批量代码,当然要先安装这2个软件啦,下面就简单说说如何安装破解这2款软件吧,当然破解只是学习之用,请大家不要用于商业用途哈 ...
- 51Nod 1256 求乘法逆元--扩展欧几里德
#include<stdio.h> int exgcd(int a,int b,int &x,int &y) { ) { x=; y=; return a; } int r ...
- MDK stm32 仿真
直接选择simulator,仿真时报错 *** error 65: access violation at 0x40021000 : no 'read' permission 修改系统配置,原配置如下 ...
- 51nod 1076 2条不相交的路径
给出一个无向图G的顶点V和边E.进行Q次查询,查询从G的某个顶点V[s]到另一个顶点V[t],是否存在2条不相交的路径.(两条路径不经过相同的边) (注,无向图中不存在重边,也就是说确定起点和终点 ...
- [BZOJ1441&BZOJ2257&BZOJ2299]裴蜀定理
裴蜀定理 对于整系数方程ax+by=m,设d =(a,b) 方程有整数解当且仅当d|m 这个定理实际上在之前学习拓展欧几里得解不定方程的时候就已经运用到 拓展到多元的方程一样适用 BZOJ1441 给 ...
- angular2框架搭建,angular-cli使用,angular2学习
angular红红火火很多年了,一眨眼ng4都出来了,我也只能叹息前端的日新月异,以及感叹自己永远追赶不上时代的步伐,但是没关系,一个优秀的前端不在于他懂的无数的框架,而在于遇到问题时候懂得如何学习, ...
- CSS3动画(重要)
CSS3 动画 CSS3,我们可以创建动画,它可以取代许多网页动画图像,Flash动画,和JAVAScripts. CSS3 @keyframes 规则 要创建CSS3动画,你将不得不了解@keyfr ...
- python-列表 字典 集合 元祖 字符串的相关总结练习
1.执行python脚本的两种方式指定解释器执行在交互器中执行 2.简述位.字节的关系:ASCII1个二进制位是计算机里的最小表示单元1个字节是计算机里最小的储存单元二进制位=8bits(位)8bit ...
- 在opensuse 中安装视频解码器
最近由于需要32位的linux系统使用,很多版本的linux都不再发布32的安装镜像了,有一些又不是很熟悉,我熟悉的manjaro发布的32镜像又不是kde桌面,最后发现opensuse的滚动版本,即 ...