zoukankan      html  css  js  c++  java
  • jdk8中Spliterator的作用

      之前的时候看集合部分源码没看完,今天又翻了一下,看到了个东西spliterator,感觉挺陌生。查了一下,网上解读源码的挺多,但没有使用的例子,于是看了下代码,准备自己写个例子试试。
    源码部分,灵小帝的博客已经说的很清楚了,摘抄如下:
    --------------------------------------------------------------------------------------------------------------------------
    Spliterator是什么
      Spliterator是一个可分割迭代器(splitable iterator),可以和iterator顺序遍历迭代器一起看。jdk1.8发布后,对于并行处理的能力大大增强,Spliterator就是为了并行遍历元素而设计的一个迭代器,jdk1.8中的集合框架中的数据结构都默认实现了spliterator,后面我们也会结合ArrayList中的spliterator()一起解析。
    Spliterator内部结构
     //单个对元素执行给定的动作,如果有剩下元素未处理返回true,否则返回false
     boolean tryAdvance(Consumer<? super T> action);
    
     //对每个剩余元素执行给定的动作,依次处理,直到所有元素已被处理或被异常终止。默认方法调用tryAdvance方法
     default void forEachRemaining(Consumer<? super T> action) {
        do { } while (tryAdvance(action));
     }
    
     //对任务分割,返回一个新的Spliterator迭代器
     Spliterator<T> trySplit();
    
     //用于估算还剩下多少个元素需要遍历
     long estimateSize();
    
     //当迭代器拥有SIZED特征时,返回剩余元素个数;否则返回-1
     default long getExactSizeIfKnown() {
        return (characteristics() & SIZED) == 0 ? -1L : estimateSize();
     }
    
      //返回当前对象有哪些特征值
     int characteristics();
    
     //是否具有当前特征值
     default boolean hasCharacteristics(int characteristics) {
        return (characteristics() & characteristics) == characteristics;
     }
     //如果Spliterator的list是通过Comparator排序的,则返回Comparator
     //如果Spliterator的list是自然排序的 ,则返回null
     //其他情况下抛错
     default Comparator<? super T> getComparator() {
         throw new IllegalStateException();
     }
      特征值其实就是为表示该Spliterator有哪些特性,用于可以更好控制和优化Spliterator的使用。关于获取比较器getComparator这一个方法,目前我还没看到具体使用的地方,所以可能理解有些误差。特征值如下:(部分属于猜测)
    ArrayListSpliterator
    static final class ArrayListSpliterator<E> implements Spliterator<E> {
        //用于存放ArrayList对象
       private final ArrayList<E> list;
       //起始位置(包含),advance/split操作时会修改
       private int index; 
       //结束位置(不包含),-1 表示到最后一个元素
       private int fence; 
       //用于存放list的modCount
       private int expectedModCount; 
    
       ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
                                 int expectedModCount) {
                this.list = list; 
                this.index = origin;
                this.fence = fence;
                this.expectedModCount = expectedModCount;
            }
        //获取结束位置(存在意义:首次初始化石需对fence和expectedModCount进行赋值)
       private int getFence() { 
            int hi; 
            ArrayList<E> lst;
            //fence<0时(第一次初始化时,fence才会小于0):
            if ((hi = fence) < 0) {
                //list 为 null时,fence=0
                if ((lst = list) == null)
                    hi = fence = 0;
                else {
                //否则,fence = list的长度。
                    expectedModCount = lst.modCount;
                    hi = fence = lst.size;
                }
            }
            return hi;
        }
        //分割list,返回一个新分割出的spliterator实例
        public ArrayListSpliterator<E> trySplit() {
            //hi为当前的结束位置
            //lo 为起始位置
            //计算中间的位置
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            //当lo>=mid,表示不能在分割,返回null
            //当lo<mid时,可分割,切割(lo,mid)出去,同时更新index=mid
            return (lo >= mid) ? null : 
                new ArrayListSpliterator<E>(list, lo, index = mid,                                         expectedModCount);
        }
        //返回true 时,只表示可能还有元素未处理
        //返回false 时,没有剩余元素处理了。。。
        public boolean tryAdvance(Consumer<? super E> action) {
             if (action == null)
                 throw new NullPointerException();
             //hi为当前的结束位置
             //i 为起始位置
             int hi = getFence(), i = index;
             //还有剩余元素未处理时
             if (i < hi) {
                 //处理i位置,index+1
                 index = i + 1;
                 @SuppressWarnings("unchecked") E e = (E)list.elementData[i];
                 action.accept(e);
                 //遍历时,结构发生变更,抛错
                 if (list.modCount != expectedModCount)
                     throw new ConcurrentModificationException();
                 return true;
             }
             return false;
         }
        //顺序遍历处理所有剩下的元素
       public void forEachRemaining(Consumer<? super E> action) {
           int i, hi, mc; // hoist accesses and checks from loop
           ArrayList<E> lst; Object[] a;
           if (action == null)
               throw new NullPointerException();
           if ((lst = list) != null && (a = lst.elementData) != null) {
               //当fence<0时,表示fence和expectedModCount未初始化,可以思考一下这里能否直接调用getFence(),嘿嘿?
               if ((hi = fence) < 0) {
                   mc = lst.modCount;
                   hi = lst.size;
               }
               else
                   mc = expectedModCount;
               if ((i = index) >= 0 && (index = hi) <= a.length) {
                   for (; i < hi; ++i) {
                       @SuppressWarnings("unchecked") E e = (E) a[i];
                       //调用action.accept处理元素
                       action.accept(e);
                   }
                   //遍历时发生结构变更时抛出异常
                   if (lst.modCount == mc)
                       return;
               }
           }
           throw new ConcurrentModificationException();
       }
    
       public long estimateSize() {
            return (long) (getFence() - index);
        }
    
        public int characteristics() {
            //打上特征值:、可以返回size
            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
        }
    }
    --------------------------------------------------------------------------------------------------------------------------
      以上为源码讲解部分,大意是说,这个就是用来多线程并行迭代的迭代器,这个迭代器的主要作用就是把集合分成了好几段,每个线程执行一段,因此是线程安全的。基于这个原理,以及modCount的快速失败机制,如果迭代过程中集合元素被修改,会抛出异常。
      我们设计一个测试用例:创建一个长度为100的list,如果下标能被10整除,则该位置数值跟下标相同,否则值为aaaa。然后多线程遍历list,取出list中的数值(字符串aaaa不要)进行累加求和。
    测试代码如下:
    public class Atest {
        AtomicInteger count = new AtomicInteger(0);
        List<String> strList = createList();
        Spliterator spliterator = strList.spliterator();
    
        /**
         * 多线程计算list中数值的和
         * 测试spliterator遍历
         */
        @Test
        public void mytest(){
            for(int i=0;i<4;i++){
                new MyThread().start();
            }
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("结果为:" + count);
        }
    
        class MyThread extends Thread{
            @Override
            public void run() {
                String threadName = Thread.currentThread().getName();
                System.out.println("线程"+threadName+"开始运行-----");
                spliterator.trySplit().forEachRemaining(new Consumer() {
                    @Override
                    public void accept(Object o) {
                        if(isInteger((String)o)){
                            int num = Integer.parseInt(o +"");
                            count.addAndGet(num);
                            System.out.println("数值:"+num+"------"+threadName);
                            try {
                                Thread.sleep(2000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
                System.out.println("线程"+threadName+"运行结束-----");
            }
        }
    
        private List<String> createList(){
            List<String> result = new ArrayList<>();
            for(int i=0; i<100; i++){
                if(i % 10 == 0){
                    result.add(i+"");
                }else{
                    result.add("aaa");
                }
            }
            return result;
        }
    
        public static boolean isInteger(String str) {
            Pattern pattern = Pattern.compile("^[-\+]?[\d]*$");
            return pattern.matcher(str).matches();
        }
    }
      输出结果:
      可以看到,有4个线程执行了该方法,并得到了正确的结果,并发遍历执行没有问题。
     
     
     

  • 相关阅读:
    XAF应用开发教程(七)外观控制模块
    XAF应用开发教程(五)验证模块
    XAF应用开发教程(六)控制器
    XAF应用开发教程(四)应用程序模型
    XAF应用开发教程(三)业务对象模型之引用类型与关联关系
    XAF应用开发教程(二)业务对象模型之简单类型属性
    XAF应用开发教程(一) 创建项目
    CSharp Similarities and Differences
    Nemerle Quick Guide
    2.1 确知信号的类型
  • 原文地址:https://www.cnblogs.com/nevermorewang/p/9368431.html
Copyright © 2011-2022 走看看