紧接上文《JAVA8 Spliterator 并行迭代器用法以及 自定义Spliterator (一)》
上文实现了demo 说明迭代器一般的切割规律,本文主要介绍如何实现自定义spliterator 以及根据自定义迭代器说明各个方法的作用,本文主要实现了一个简化自定义迭代器MySpliterator 功能简单,主要是为了说明,大家个根据自己业务需求开发
MySpliterator 功能:
1.仅可且只能进行两次分割,超过两次就不允许分割(实际情况肯定不肯是这样,需要根据总数进行切割次数计算,但建议先计算出线程数然后再计算切割次数)
2.输出遍历数据以便查看情况
闲话不多说,上代码
/** * @desc: 自定义Spliterator ,只能进行一次切割,然后输出遍历元素 */ class MySpliterator implements Spliterator { private int[] array; private int origin; private int fence; private boolean canSplit = true; private String threadName; private MySpliterator(int[] array, String threadName) { this.origin = 0; this.array = array; this.fence = array.length; this.threadName = threadName; } private MySpliterator(int[] array, boolean canSplit, String threadName) { this.origin = 0; this.array = array; this.fence = array.length - 1; this.canSplit = canSplit; this.threadName = threadName; } /** * @desc: 处理具体某元素,如果成功则数据角标+1并返回true,失败则返回false,相当于iterator hasNext和next一体了 */ @Override public boolean tryAdvance(Consumer action) { if (origin <= fence) { int x = array[origin++]; action.accept(x); System.out.println(threadName + ":" + x); return true; } else return false; } /** * @desc: 切割数据为一半一半,如果切割过一次则canSplit变为false,下次再切割则返回null */ @Override public Spliterator trySplit() { //计算中间值 int mid = ((origin + fence) >>> 1) & ~1; fence = mid; System.out.println("进行一次切割"); return new MySpliterator(Arrays.copyOfRange(array, mid + 1, array.length), false, "线程2"); } /** * @desc: 剩余数量,用于评估是否调用切割功能,如果返回<=0则无法切割,将不会调用trySplit */ @Override public long estimateSize() { if (canSplit) { canSplit = false; return (fence - origin) / 2; } else { return 0; } } /** * CONCURRENT 特征值表示可以通过多个线程安全同时修改元素源(允许添加,替换和/或删除),而无需外部同步。 * DISTINCT 特性值这标志着,对于每对遇到的元件 x, y , !x.equals(y) 。 * IMMUTABLE 特征值表示元素源不能在结构上进行修改; 也就是说,不能添加,替换或删除元素,因此在遍历过程中不会发生这种更改。 * NONNULL 特征值表示源保证遇到的元素不会为 null * ORDERED 特征值表示为元素定义遇到顺序 * SIZED 表示在遍历或 estimateSize()之前从 estimateSize()返回的值的特征值表示在没有结构源修改的情况下表示完全遍历将遇到的元素数量的精确计数的有限大小。 * SORTED 特征值表示遇到的顺序遵循定义的排序顺序。 * SUBSIZED 特征值这标志着从产生的所有Spliterators trySplit()将是既 SIZED和 SUBSIZED */ @Override public int characteristics() { return ORDERED | SIZED | IMMUTABLE | SUBSIZED; } }
@Test public void streamMySpliteratorTest() { int[] array = IntStream.range(0, 100).toArray(); MySpliterator mySpliterator = new MySpliterator(array, "线程1"); Stream<Integer> stream = StreamSupport.stream(mySpliterator, true); List<Integer> integers = stream.map(x -> x+1).collect(Collectors.toList()); Assert.assertEquals(integers.size(),100); }
输出
进行一次切割 线程1:0 线程1:1 线程1:2 线程1:3 线程1:4 线程1:5 线程1:6 线程1:7 线程1:8 线程1:9 线程1:10 线程1:11 线程1:12 线程1:13 线程1:14 线程1:15 线程1:16 线程1:17 线程1:18 线程1:19 线程2:51 线程1:20 线程1:21 线程1:22 线程1:23 线程1:24 线程1:25 线程1:26 线程1:27 线程1:28 线程1:29 线程1:30 线程1:31 线程1:32 线程1:33 线程1:34 线程2:52 线程1:35 线程1:36 线程2:53 线程2:54 线程2:55 线程2:56 线程2:57 线程2:58 线程2:59 线程2:60 线程2:61 线程2:62 线程2:63 线程2:64 线程1:37 线程2:65 线程2:66 线程2:67 线程2:68 线程2:69 线程2:70 线程2:71 线程2:72 线程2:73 线程2:74 线程2:75 线程2:76 线程2:77 线程2:78 线程2:79 线程2:80 线程2:81 线程2:82 线程2:83 线程2:84 线程2:85 线程2:86 线程2:87 线程2:88 线程2:89 线程2:90 线程2:91 线程2:92 线程2:93 线程2:94 线程2:95 线程1:38 线程1:39 线程1:40 线程1:41 线程1:42 线程1:43 线程1:44 线程1:45 线程1:46 线程1:47 线程1:48 线程1:49 线程1:50 线程2:96 线程2:97 线程2:98 线程2:99
看到结果确实是只切割了一次,并且是平均切割,何时启动触发的
tryAdvance 呢,是在启动遍历的时候或者统计的时候比如
在调用这个时候就会开始启动遍历,然后map的consumer 会映射到到spliterator的tryadance
如下