深入扒 HashMap 源码 - 6.5 HashMap 内部类分割迭代器 HashMapSpliterator、KeySpliterator、ValueSpliterat、EntrySplitera

首先说说作用,原来的 HashMap 已经有了迭代器了,为什么还要这些迭代器?难道是爱吗?是责任吗?(づ。◕‿‿◕。)づ

哈哈哈,李白说过,存在即合理

为了适应时代的变化,现在并行计算越来越需要,这个迭代器的诞生也是为了并行迭代而出现的,可以在多线程的情况下迭代同一个 HashMap,但是官方建议一个线程只和一个迭代器配合使用啦

好,入正题,在这里 KeySpliterator、ValueSpliterat、EntrySplitera 都是继承于 HashMapSpliterator,这里只对 KeySpliterator 和 HashMapSpliterator 的源码解析,另外两个原理都一样,几题请看官们看下面哎~

static class HashMapSpliterator<K,V> {
    // 需要遍历的 HashMap 对象
    final HashMap<K,V> map;
    // 当前正在遍历的节点
    Node<K,V> current;          // current node
    // 当前迭代器开始遍历的桶索引
    int index;                  // current index, modified on advance/split
    // 当前迭代器遍历上限的桶索引
    int fence;                  // one past last index
    // 需要遍历的元素个数,暂时没有发现用处比较大的地方
    int est;                    // size estimate
    // 期望操作数,用于多线程情况下,如果多个线程同时对 HashMap 进行读写,
    // 那么这个期望操作数 expectedModCount 和 HashMap 的 modCount 就会不一致,这时候抛个异常出来,称为“快速失败”
    int expectedModCount;       // for comodification checks

    // 初始化
    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;
    }

    // 获取栅栏?不不不,这个方法的作用是获取一个当前迭代器的一个迭代范围,例如返回的值是 4,那么遍历到第四个桶就会结束
    // 如果 table 有数据的话,貌似返回的值永远都是一样的
    final int getFence() { // initialize fence and size on first use
        int hi;
        // 第一个分割迭代器会执行下面 if 内的代码
        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(); // force init
        return (long) est;
    }
}

static final class KeySpliterator<K,V> extends HashMapSpliterator<K,V> implements Spliterator<K> {
    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);
    }

    // 在当前迭代器遍历范围遍历一遍
    public void forEachRemaining(Consumer<? super K> action) { 
        int i, hi, mc;
        if (action == null)
            throw new NullPointerException();
        HashMap<K,V> m = map;
        Node<K,V>[] tab = m.table;
        if ((hi = fence) < 0) {
            mc = expectedModCount = m.modCount;
            hi = fence = (tab == null) ? 0 : tab.length;
        }
        else
            mc = expectedModCount;
        if (tab != null && tab.length >= hi &&
            (i = index) >= 0 && (i < (index = hi) || current != null)) {
            Node<K,V> p = current;
            current = null;
            do {
                if (p == null)
                    p = tab[i++];
                else {
                    action.accept(p.key);
                    p = p.next;
                }
            } while (p != null || i < hi);
            if (m.modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    // 会遍历迭代器遍历的范围之内的元素,当找到第一个非空元素的时候就会停止遍历
    public boolean tryAdvance(Consumer<? super K> action) { 
        int hi;
        if (action == null)
            throw new NullPointerException();
        Node<K,V>[] tab = map.table;
        if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
            while (current != null || index < hi) {
                if (current == null)
                    current = tab[index++];
                else {
                    K k = current.key;
                    current = current.next;
                    action.accept(k);
                    if (map.modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    return true;
                }
            }
        }
        return false;
    }
    
    // 技术有限,暂时还不清楚有啥用,网上一大片说什么特征码什么的,但是没有发现哪里有用到,
    // 难道做拓展是要用到?再底下的一层遍历的时候要用到?有点不明不白,不甘心哎~
    public int characteristics() { 
        return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | Spliterator.DISTINCT;
    }
}

测试代码

    static class MyThread implements Runnable {
        Spliterator<Integer> spliterator;
        String threadName;
        
        MyThread(Spliterator<Integer> spliterator, String threadName) {
            this.spliterator = spliterator;
            this.threadName = threadName;
        }
        @Override
        public void run() {
            spliterator.forEachRemaining(s -> {
                System.out.println(threadName + "=" + s);
            });
        }
    }
    public static void main(String[] args) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < 23; i++) {
            map.put(i, i);
        }
        Spliterator<Integer> s1 = map.keySet().spliterator();
        Spliterator<Integer> s2 = s1.trySplit();
        Spliterator<Integer> s3 = s2.trySplit();
        
        Thread t1 = new Thread(new MyThread(s1, "线程1"));
        Thread t2 = new Thread(new MyThread(s2, "线程2"));
        Thread t3 = new Thread(new MyThread(s3, "线程3"));
        t1.start();
        t2.start();
        t3.start();
    }

输出结果

线程2=8
线程2=9
线程2=10
线程2=11
线程2=12
线程2=13
线程2=14
线程1=16
线程2=15
线程3=0
线程1=17
线程1=18
线程1=19
线程1=20
线程1=21
线程3=1
线程1=22
线程3=2
线程3=3
线程3=4
线程3=5
线程3=6
线程3=7

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值