java 8 stream中的Spliterator简介

简介: java 8 stream中的Spliterator简介

目录



java 8 stream中的Spliterator简介


简介


Spliterator是在java 8引入的一个接口,它通常和stream一起使用,用来遍历和分割序列。


只要用到stream的地方都需要Spliterator,比如List,Collection,IO channel等等。


我们先看一下Collection中stream方法的定义:


default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }


default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }


我们可以看到,不管是并行stream还是非并行stream,都是通过StreamSupport来构造的,并且都需要传入一个spliterator的参数。


好了,我们知道了spliterator是做什么的之后,看一下它的具体结构:


image.png


spliterator有四个必须实现的方法,我们接下来进行详细的讲解。


tryAdvance


tryAdvance就是对stream中的元素进行处理的方法,如果元素存在,则对他进行处理,并返回true,否则返回false。


如果我们不想处理stream后续的元素,则在tryAdvance中返回false即可,利用这个特征,我们可以中断stream的处理。这个例子我将会在后面的文章中讲到。


trySplit


trySplit尝试对现有的stream进行分拆,一般用在parallelStream的情况,因为在并发stream下,我们需要用多线程去处理stream的不同元素,trySplit就是对stream中元素进行分拆处理的方法。


理想情况下trySplit应该将stream拆分成数目相同的两部分才能最大提升性能。


estimateSize


estimateSize表示Spliterator中待处理的元素,在trySplit之前和之后一般是不同的,后面我们会在具体的例子中说明。


characteristics


characteristics表示这个Spliterator的特征,Spliterator有8大特征:


public static final int ORDERED    = 0x00000010;//表示元素是有序的(每一次遍历结果相同)
public static final int DISTINCT   = 0x00000001;//表示元素不重复
public static final int SORTED     = 0x00000004;//表示元素是按一定规律进行排列(有指定比较器)
public static final int SIZED      = 0x00000040;//
表示大小是固定的
public static final int NONNULL    = 0x00000100;//表示没有null元素
public static final int IMMUTABLE  = 0x00000400;//表示元素不可变
public static final int CONCURRENT = 0x00001000;//表示迭代器可以多线程操作
public static final int SUBSIZED   = 0x00004000;//表示子Spliterators都具有SIZED特性


一个Spliterator可以有多个特征,多个特征进行or运算,最后得到最终的

characteristics。


举个例子


上面我们讨论了Spliterator一些关键方法,现在我们举一个具体的例子:


@AllArgsConstructor
@Data
public class CustBook {
    private String name;
}


先定义一个CustBook类,里面放一个name变量。


定义一个方法,来生成一个CustBook的list:


public static List<CustBook> generateElements() {
        return Stream.generate(() -> new CustBook("cust book"))
                .limit(1000)
                .collect(Collectors.toList());
    }


我们定义一个call方法,在call方法中调用了tryAdvance方法,传入了我们自定义的处理方法。这里我们修改book的name,并附加额外的信息。


public String call(Spliterator<CustBook> spliterator) {
        int current = 0;
        while (spliterator.tryAdvance(a -> a.setName("test name"
                .concat("- add new name")))) {
            current++;
        }
        return Thread.currentThread().getName() + ":" + current;
    }


最后,写一下测试方法:


@Test
    public void useTrySplit(){
        Spliterator<CustBook> split1 = SpliteratorUsage.generateElements().spliterator();
        Spliterator<CustBook> split2 = split1.trySplit();
        log.info("before tryAdvance: {}",split1.estimateSize());
        log.info("Characteristics {}",split1.characteristics());
        log.info(call(split1));
        log.info(call(split2));
        log.info("after tryAdvance {}",split1.estimateSize());
    }


运行的结果如下:


23:10:08.852 [main] INFO com.flydean.SpliteratorUsage - before tryAdvance: 500
23:10:08.857 [main] INFO com.flydean.SpliteratorUsage - Characteristics 16464
23:10:08.858 [main] INFO com.flydean.SpliteratorUsage - main:500
23:10:08.858 [main] INFO com.flydean.SpliteratorUsage - main:500
23:10:08.858 [main] INFO com.flydean.SpliteratorUsage - after tryAdvance 0


List总共有1000条数据,调用一次trySplit之后,将List分成了两部分,每部分500条数据。


注意,在tryAdvance调用之后,estimateSize变为0,表示所有的元素都已经被处理完毕。


再看一下这个Characteristics=16464,转换为16进制:Ox4050 = ORDERED or SIZED or SUBSIZED 这三个的或运算。


这也是ArrayList的基本特征。


总结


本文介绍了跟stream息息相关的接口Spliterator,讨论了它的构成,并举例说明,希望大家能够掌握。


本文的例子https://github.com/ddean2009/learn-java-streams/tree/master/Spliterator

相关文章
|
20天前
|
存储 Java API
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
198 92
|
20天前
|
存储 Java API
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
140 68
|
2月前
|
Oracle Java 关系型数据库
掌握Java Stream API:高效集合处理的利器
掌握Java Stream API:高效集合处理的利器
329 80
|
2月前
|
安全 Java API
Java 8 Stream API:高效集合处理的利器
Java 8 Stream API:高效集合处理的利器
221 83
|
2月前
|
人工智能 安全 Java
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
88 24
|
3月前
|
SQL JSON 安全
Java 8 + 中 Lambda 表达式与 Stream API 的应用解析
摘要:本文介绍了Java 8+核心新特性,包括Lambda表达式与Stream API的集合操作(如过滤统计)、函数式接口的自定义实现、Optional类的空值安全处理、接口默认方法与静态方法的扩展能力,以及Java 9模块化系统的组件管理。每个特性均配有典型应用场景和代码示例,如使用Stream统计字符串长度、Optional处理Map取值、模块化项目的依赖声明等,帮助开发者掌握现代Java的高效编程范式。(150字)
61 1
|
3月前
|
存储 Java 大数据
Java代码优化:for、foreach、stream使用法则与性能比较
总结起来,for、foreach和stream各自都有其适用性和优势,在面对不同的情况时,有意识的选择更合适的工具,能帮助我们更好的解决问题。记住,没有哪个方法在所有情况下都是最优的,关键在于理解它们各自的特性和适用场景。
368 23
|
2月前
|
SQL 人工智能 Rust
Java 开发中Stream的toMap与Map 使用技巧
本文深入解析了 Java 中 `toMap()` 方法的三大问题:重复键抛出异常、`null` 值带来的风险以及并行流中的性能陷阱,并提供了多种替代方案,如使用 `groupingBy`、`toConcurrentMap` 及自定义收集器,帮助开发者更安全高效地进行数据处理。
148 0
|
2月前
|
人工智能 Java
java中static关键字简介
`static`关键字用于修饰类的成员变量和方法,使其属于类而非对象。静态成员可通过类名直接访问,无需实例化对象。静态方法只能访问静态成员,不能直接访问非静态成员或使用`this`关键字。此外,静态代码块在类首次加载时执行且仅执行一次,适用于初始化操作。
|
6月前
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
305 29
JVM简介—1.Java内存区域

热门文章

最新文章