Java集合深入学习 - HashMap源码解析-1基础(基于jdk1.8)
Java集合深入学习 - HashMap源码解析-2查找数据(基于jdk1.8)
Java集合深入学习 - HashMap源码解析-3添加与扩容(基于jdk1.8)
Java集合深入学习 - HashMap源码解析-4删改与遍历(基于jdk1.8)
1.修改数据
直接的put方法,如果key对应的节点已经存在,会直接覆盖其value值,节点不存在则会直接添加数据到最后,可以说put操作为添加或修改,直接的修改数据可以用replace方法,代码如下:
/**
* 修改数据,按key对比,修改对应value值
*/
@Override
public V replace(K key, V value) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) != null) { //获取节点
V oldValue = e.value;
e.value = value; //修改节点value值
afterNodeAccess(e);//为继承HashMap的LinkedHashMap类服务的方法调用
return oldValue;
}
return null;
}
/**
* 修改数据 key = key,value = oldValue的节点,更新value值为newValue
*/
@Override
public boolean replace(K key, V oldValue, V newValue) {
Node<K,V> e; V v;
if ((e = getNode(hash(key), key)) != null && //获取节点
((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) { //value比较
e.value = newValue; //更新节点value
afterNodeAccess(e);//为继承HashMap的LinkedHashMap类服务的方法调用
return true;
}
return false;
}
2.删除数据
还是直接上代码比较实在
/**
* 删除节点 按key查找 直接删除
*/
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
/**
* 删除节点 key = key,value = value
*/
@Override
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
/**
* 删除节点
* @param hash key的hash值
* @param key key
* @param value value该值是否作为删除的条件取决于matchValue是否为true
* @param matchValue 如果为true,则当key对应的节点的值equals(value)为true时才删除;否则不关心value的值
* @param movable 删除后是否移动节点,如果为false,则不移动
* @return
*/
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;
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) //红黑树结构
node = ((TreeNode<K,V>)p).getTreeNode(hash, key); //获取到对应节点
else {
do { //遍历链表查找对应节点
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) { //查找到节点,value值进行校验
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);
return node;
}
}
return null;
}
3. 遍历
3.1 通过map.keySet()获取Map中key的集合,对key进行遍历时,可通过map.get(key) 获取对应value值
public static void main(String[] args) {
Map<String, Object> map = new HashMap<String, Object>();
Set<String> set = map.keySet(); //获取key集合
for (String key : set) { //遍历
System.out.println("key -> value === " + key + " -> " + map.get(key));
}
}
map.keySet()方法探究,竟然发现。。。。。。
transient Set<K> keySet;
/**
* 获取集合中key的set集合 其中Set是HashMap的内部类KeySet
*/
public Set<K> keySet() {
Set<K> ks = keySet; //获取keySet
if (ks == null) { //keySet为空,对其进行初始化
ks = new KeySet();
keySet = ks; //赋值Map中的keySet集合
}
return ks;
}
小朋友?你是否有很多问号???我也有很多的问号,于是我继续看了KeySet这个HashMap的内部类,打算一探究竟。
/**
* 定义内部类KeySet实现AbstractSet
*/
final class KeySet extends AbstractSet<K> {
/** 返回当前Map的size */
public final int size() { return size; }
/** 清空当前HashMap */
public final void clear() { HashMap.this.clear(); }
/** 返回一个新的KeyIterator对象*/
public final Iterator<K> iterator() { return new KeyIterator(); }
/** 调用HashMap中的containsKey方法,判断是否存在当前key */
public final boolean contains(Object o) { return containsKey(o); }
/** 删除HashMap中key对应的节点信息 */
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
/** 初始化一个 KeySpliterator迭代器*/
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
/** 遍历HashMap中的所有节点 进行Consumer*/
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) { //hashMap中有数据
int mc = modCount;
for (int i = 0; i < tab.length; ++i) { //遍历HashMap中的数组
for (Node<K,V> e = tab[i]; e != null; e = e.next) //遍历数组中的链表
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
大眼看去,除了各种操作都是直接作用到map上,还是未看出个所以然,不过,我记得没错的话,set集合的遍历只能通过迭代器或者增强for循环(听说也是使用迭代器实现的,也不知道是不是真的),于是看一下迭代器相关的KeyIterator和KeySpliterator,仔细一看,KeySpliterator的构造方法传入了当前HashMap.this,我觉得有戏,咱们看下去
/**
* 定义内部类KeySpliterator继承HashMapSpliterator实现Spliterator
*/
static final class KeySpliterator<K,V>
extends HashMapSpliterator<K,V>
implements Spliterator<K> {
/** 构造 直接调用HashMapSpliterator的构造方法*/
KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
}
// 对当前迭代器进行分割
public KeySpliterator<K,V> trySplit() {
//获取得当前迭代器的开始索引和最后索引 计算中间值
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
//校验索引并将操作数est也取一半
return (lo >= mid || current != null) ? null :
new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
expectedModCount);
}
/** 其他方法略掉了*/
}
/**
* 定义内部类HashMapSpliterator分割迭代器
*/
static class HashMapSpliterator<K,V> {
final HashMap<K,V> map; // 遍历的HashMap对象
Node<K,V> current; // 当前节点
int index; // 当前迭代器开始遍历的桶索引
int fence; // 当前迭代器遍历上限的桶索引
int est; // 需要遍历的元素个数
int expectedModCount; // 操作次数
/** 构造方法 */
HashMapSpliterator(HashMap<K,V> m, int origin,
int fence, int est,
int expectedModCount) {
this.map = m;
this.index = origin;
this.fence = fence;
this.est = est;
this.expectedModCount = expectedModCount;
}
/**
* 初始化迭代器遍历上限的桶索引 若值不合理,则取HashMap的数组大小
*/
final int getFence() {
int hi;
if ((hi = fence) < 0) {
HashMap<K,V> m = map;
est = m.size;
expectedModCount = m.modCount;
Node<K,V>[] tab = m.table;
hi = fence = (tab == null) ? 0 : tab.length;
}
return hi;
}
// 获取当前迭代器需要遍历的元素个数
public final long estimateSize() {
getFence(); // 初始化迭代器遍历上限
return (long) est;
}
}
这里是分割迭代,再看看另一个KeyIterator吧
/**
* 定义一个内部类 KeyIterator继承HashIterator实现了Iterator接口
*/
final class KeyIterator extends HashIterator
implements Iterator<K> {
/** 重写next方法 调用HashIterator的 nextNode方法返回节点的key */
public final K next() { return nextNode().key; }
}
/**
* 定义内部类HashIterator
*/
abstract class HashIterator {
Node<K,V> next; // 下一个节点
Node<K,V> current; // 当前迭代节点
int expectedModCount; // 操作数for fast-fail
int index; // 索引位置
/** 构造方法 */
HashIterator() {
expectedModCount = modCount; //初始化操作次数
Node<K,V>[] t = table; //获取Map的数组
current = next = null; //初始化参数
index = 0; //初始化迭代索引位置
if (t != null && size > 0) { // 找到table中一个节点赋值给next
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();
if ((next = (current = e).next) == null && (t = table) != null) { //遍历当前index下的链表或红黑树只到找不到下一个节点
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的removeNode方法删除节点
expectedModCount = modCount;
}
}
哦呵呵,真相大白了,原来keySet方法返回的set是HashMap定义的内部类,其内部迭代器也是使用的HashMap定义的内部迭代器,该迭代器中实现了hashMap遍历的相关方法。
3.2其他遍历方式
上面说的map.keySet(),而后对返回的Set集合进行遍历,同样原理的还有map.entrySet(),返回结果是节点集合,直接包含key,value等相关值。其具体实现原理同map.keySet()实现原理一致。如果只获取value集合,还有一个map.values()方法,返回所有value的集合,其实现方式与上面两种方法类似,其底层都是继承自同一个处理的迭代器HashIterator。
本文链接,转载请标注https://blog.csdn.net/luo_mu_hpu/article/details/106256709