1. 簡(jiǎn)介
我們常常遍歷數(shù)組,集合,map等,都是在單線程里面遍歷的 ,jdk1.8 之后,出現(xiàn)Spilterator 可以讓我們?cè)诙嗑€程下遍歷集合,基本思想就是把一個(gè)集合分割成多個(gè)小集合由多個(gè)線程遍歷
2.接口方法介紹
// 如果有,則遍歷下一個(gè)元素
tryAdvance
// 遍歷 剩余的元素
forEachRemaining
// 就是嘗試分割, 分割失敗(元素太少,或者已經(jīng)開始遍歷,或者獲得分割器x效率低下)返回null ,每次分割一半
trySplit
// 返回估算的大小,如果SIZED 和SUBSIZED 那么返回 大小是確定的
estimateSize
// 就是調(diào)用上面的方法, 如果不確定那么返回-1
getExactSizeIfKnown
//這個(gè)分割器的特性
characteristics
// 判斷此分割器是否含有這個(gè)特性 (使用 按位與操作的)
hasCharacteristics
// 獲取比較器,如果是自然排序的,返回null, 如果是比較器排序的返回比較器,否則跑出一次
getComparator
3. 分割器的特性介紹
// 指定這個(gè)分割器按照順序取出元素 如數(shù)組,List產(chǎn)生的Spiterator都是有這個(gè)特性的
ORDERED
// 不重復(fù) 如Set
DISTINCT
// 排好序的 如 Set
SORTED
// 有固定大小的 ,我們常常使用非多線程安全集合,如Set,List都是,并發(fā)的集合在Spilerator分割和遍歷的時(shí)候是會(huì)發(fā)生變化的
SIZED
// 里面的元素不是空的
NONNULL
// 集合是不可變的 ,那么也具有 SIZED和SUBSIZED
IMMUTABLE
// 支持并發(fā)的 ,那么一般沒有SIZED和SUBSIZED 如ConcurrentSkipListSet
CONCURRENT、
// 分割后的子大小也是不變的
SUBSIZED
注意 : 子Spliterator 與父Spliterator的特性可能不一樣
4.子接口介紹
這些子接口使用的是原生類型,執(zhí)行速度比包裝類型快很多
4.1OfPrimitive
public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
extends Spliterator<T>
// T :我們?cè)氐念愋? // T_CONS: 我們forEachRemaining 消費(fèi)的類型,是IntConsumer或者LongConsumer或者DoubleCosumer中的一個(gè)
// T_SPLITR : OfPrimitive子接口的類型
// 其實(shí)這個(gè)接口就起抽象 原子類型的Spiterator
4.2OfInt
對(duì)OfPrimitive的實(shí)現(xiàn) T 用的是Integer ,而且還添加了一個(gè)boolean
tryAdvance(IntConsumer action);注意參數(shù)是IntConsumer
默認(rèn)實(shí)現(xiàn)了Spilterator的tryAdvance()方法,實(shí)現(xiàn)如下
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);
}
這個(gè)方法的實(shí)現(xiàn)很奇怪,奇怪是action 實(shí)現(xiàn)了Consumer接口,又實(shí)現(xiàn)了IntConsumer,然而Consumer和IntConsumer不是父子關(guān)系,其實(shí)這個(gè)兩個(gè)接口的lamdab表示式子是相同的
Consumer consumer = System.out::println;
IntConsumer intConsumer = System.out::println;
// 但是這個(gè)兩個(gè)之間是不能相互強(qiáng)轉(zhuǎn)的 ,所以認(rèn)為是這樣的有一個(gè)子類實(shí)現(xiàn)了著兩個(gè)接口即可,便可以理解了
static class ConsumerImpl implements Consumer<Integer>,IntConsumer{
@Override
public void accept(Integer value) {
}
@Override
public void accept(int value) {
}
}
4.3 OfLong
跟OfInt一樣 ,只不過(guò) T 就是Long ,而 Consumer是就是LongConsumer
4.4OfDouble
跟OfInt一樣 ,只不過(guò) T 就是Double,而 Consumer是就是DoubleConsumer
5 Spliterotors介紹
這類和Spliterator 的關(guān)系就像 Collections 和Collection的關(guān)系,他是Spliterator的伴生對(duì)象。作用就是用來(lái)創(chuàng)建Spliterator的
// Spliterator.spliterator 方法 有很多重載方法,基于原生類型和引用類型的數(shù)組,還有集合的等等
// 如: 從int[]中創(chuàng)建 ,指定特行一般就是 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 簡(jiǎn)單HelloWorld
這個(gè)例子是用 Spliterator 和 ForkJoin結(jié)合使用的。他們的特性剛剛切合
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個(gè)隨機(jī)數(shù)
Random random = new Random();
int[] array = IntStream.range(0, 1000).map(i-> random.nextInt(100)).toArray();
// 創(chuàng)建分割器
Spliterator.OfInt sumSpliterator = Spliterators.spliterator(array, Spliterator.ORDERED | Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.NONNULL);
Sum sum = new Sum(sumSpliterator);
// 因?yàn)槲覀兪褂玫腇orkJoinTask ,所以我們要?jiǎng)?chuàng)建 ForkJoinPool
ForkJoinPool commonPool = ForkJoinPool.commonPool();
sum = (Sum) commonPool.submit(sum);
System.out.println(sum.join());
}
/**
* 因?yàn)檫@里使用是原生類型 ,所以我們使用原生類型Spliterator
*/
static class Sum extends RecursiveTask<Long> {
private Spliterator.OfInt spiterator;
public Sum(Spliterator.OfInt spiterator) {
this.spiterator = spiterator;
}
@Override
protected Long compute() {
// 如果是100個(gè)以內(nèi),直接計(jì)算 原生類型的數(shù)組的大小是不會(huì)發(fā)生變化的,所以這個(gè)是精確的
if (spiterator.estimateSize() <= 100) {
long[] sum = new long[1];
spiterator.forEachRemaining((IntConsumer) i -> {
sum[0] += i;
});
return sum[0];
}
// 進(jìn)行分割
Spliterator.OfInt split = spiterator.trySplit();
if (split != null) {
Sum left = new Sum(spiterator);
Sum right = new Sum(split);
//計(jì)算
left.fork();
//計(jì)算
right.fork();
//等待結(jié)果
return left.join() +
right.join();
}
return 0L;
}
}
}
7.深入使用
暫時(shí)還沒有想到以后再補(bǔ)吧。。。。
8.總結(jié)
Spliterator的主要作用就是為了提高遍歷速度,當(dāng)每次分兩塊大小相等的時(shí)候,效率是最高的,但是這是理想情況,有時(shí),當(dāng)數(shù)據(jù)結(jié)構(gòu)不理想的時(shí)候,或者分割相差太大,那么返回會(huì)降低效率。所以我們要在合適的場(chǎng)景下使用。
有什么錯(cuò)誤的地方望大佬指正,相互學(xué)習(xí)嘛。