1. 简介
我们常常遍历数组,集合,map等,都是在单线程里面遍历的 ,jdk1.8 之后,出现Spilterator 可以让我们在多线程下遍历集合,基本思想就是把一个集合分割成多个小集合由多个线程遍历
2.接口方法介绍
// 如果有,则遍历下一个元素
tryAdvance
// 遍历 剩余的元素
forEachRemaining
// 就是尝试分割, 分割失败(元素太少,或者已经开始遍历,或者获得分割器x效率低下)返回null ,每次分割一半
trySplit
// 返回估算的大小,如果SIZED 和SUBSIZED 那么返回 大小是确定的
estimateSize
// 就是调用上面的方法, 如果不确定那么返回-1
getExactSizeIfKnown
//这个分割器的特性
characteristics
// 判断此分割器是否含有这个特性 (使用 按位与操作的)
hasCharacteristics
// 获取比较器,如果是自然排序的,返回null, 如果是比较器排序的返回比较器,否则跑出一次
getComparator
3. 分割器的特性介绍
// 指定这个分割器按照顺序取出元素 如数组,List产生的Spiterator都是有这个特性的
ORDERED
// 不重复 如Set
DISTINCT
// 排好序的 如 Set
SORTED
// 有固定大小的 ,我们常常使用非多线程安全集合,如Set,List都是,并发的集合在Spilerator分割和遍历的时候是会发生变化的
SIZED
// 里面的元素不是空的
NONNULL
// 集合是不可变的 ,那么也具有 SIZED和SUBSIZED
IMMUTABLE
// 支持并发的 ,那么一般没有SIZED和SUBSIZED 如ConcurrentSkipListSet
CONCURRENT、
// 分割后的子大小也是不变的
SUBSIZED
注意 : 子Spliterator 与父Spliterator的特性可能不一样
4.子接口介绍
这些子接口使用的是原生类型,执行速度比包装类型快很多
4.1OfPrimitive
public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
extends Spliterator<T>
// T :我们元素的类型
// T_CONS: 我们forEachRemaining 消费的类型,是IntConsumer或者LongConsumer或者DoubleCosumer中的一个
// T_SPLITR : OfPrimitive子接口的类型
// 其实这个接口就起抽象 原子类型的Spiterator
4.2OfInt
对OfPrimitive的实现 T 用的是Integer ,而且还添加了一个boolean
tryAdvance(IntConsumer action);注意参数是IntConsumer
默认实现了Spilterator的tryAdvance()方法,实现如下
default boolean tryAdvance(Consumer<? super Integer> action) {
if (action instanceof IntConsumer) {
return tryAdvance((IntConsumer) action);
}
else {
if (Tripwire.ENABLED)
Tripwire.trip(getClass(),
"{0} calling Spliterator.OfInt.tryAdvance((IntConsumer) action::accept)");
return tryAdvance((IntConsumer) action::accept);
}
这个方法的实现很奇怪,奇怪是action 实现了Consumer接口,又实现了IntConsumer,然而Consumer和IntConsumer不是父子关系,其实这个两个接口的lamdab表示式子是相同的
Consumer consumer = System.out::println;
IntConsumer intConsumer = System.out::println;
// 但是这个两个之间是不能相互强转的 ,所以认为是这样的有一个子类实现了着两个接口即可,便可以理解了
static class ConsumerImpl implements Consumer<Integer>,IntConsumer{
@Override
public void accept(Integer value) {
}
@Override
public void accept(int value) {
}
}
4.3 OfLong
跟OfInt一样 ,只不过 T 就是Long ,而 Consumer是就是LongConsumer
4.4OfDouble
跟OfInt一样 ,只不过 T 就是Double,而 Consumer是就是DoubleConsumer
5 Spliterotors介绍
这类和Spliterator 的关系就像 Collections 和Collection的关系,他是Spliterator的伴生对象。作用就是用来创建Spliterator的
// Spliterator.spliterator 方法 有很多重载方法,基于原生类型和引用类型的数组,还有集合的等等
// 如: 从int[]中创建 ,指定特行一般就是 SIZED |SUBSIZE|ORDERED|NONULL
Spliterator.OfInt spliterator(int[] array, int additionalCharacteristics)
Spliterator<T> spliterator(Object[] array, int additionalCharacteristics)
spliterator(Collection<? extends T> c, int characteristics)
//等等。。
6 简单HelloWorld
这个例子是用 Spliterator 和 ForkJoin结合使用的。他们的特性刚刚切合
package liusheng.main.hello;
import java.util.Random;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.function.IntConsumer;
import java.util.stream.IntStream;
public class SpliteratorHelloWorld {
public static void main(String[] args) {
// 我们生成1000个随机数
Random random = new Random();
int[] array = IntStream.range(0, 1000).map(i-> random.nextInt(100)).toArray();
// 创建分割器
Spliterator.OfInt sumSpliterator = Spliterators.spliterator(array, Spliterator.ORDERED | Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.NONNULL);
Sum sum = new Sum(sumSpliterator);
// 因为我们使用的ForkJoinTask ,所以我们要创建 ForkJoinPool
ForkJoinPool commonPool = ForkJoinPool.commonPool();
sum = (Sum) commonPool.submit(sum);
System.out.println(sum.join());
}
/**
* 因为这里使用是原生类型 ,所以我们使用原生类型Spliterator
*/
static class Sum extends RecursiveTask<Long> {
private Spliterator.OfInt spiterator;
public Sum(Spliterator.OfInt spiterator) {
this.spiterator = spiterator;
}
@Override
protected Long compute() {
// 如果是100个以内,直接计算 原生类型的数组的大小是不会发生变化的,所以这个是精确的
if (spiterator.estimateSize() <= 100) {
long[] sum = new long[1];
spiterator.forEachRemaining((IntConsumer) i -> {
sum[0] += i;
});
return sum[0];
}
// 进行分割
Spliterator.OfInt split = spiterator.trySplit();
if (split != null) {
Sum left = new Sum(spiterator);
Sum right = new Sum(split);
//计算
left.fork();
//计算
right.fork();
//等待结果
return left.join() +
right.join();
}
return 0L;
}
}
}
7.深入使用
暂时还没有想到以后再补吧。。。。
8.总结
Spliterator的主要作用就是为了提高遍历速度,当每次分两块大小相等的时候,效率是最高的,但是这是理想情况,有时,当数据结构不理想的时候,或者分割相差太大,那么返回会降低效率。所以我们要在合适的场景下使用。
有什么错误的地方望大佬指正,相互学习嘛。