Java集合深入学习 - HashMap源码解析-4删改与遍历(基于jdk1.8)

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

猜你喜欢

转载自blog.csdn.net/luo_mu_hpu/article/details/106256709
今日推荐