关于stream说了很多,这里简单做补充,中间操作、终止操作。没有调用终止操作的情况下,中间操作也不会执行,这也可以叫惰性求值。
关于流的常见创建有如下方法:
方法 | |
集合 | Collection.stream/parallelStream |
数组 | Arrays.stream |
纯数字流 | IntStream/LongStream.range/rangeClosed Random.ints/longs/doubles |
自己创建 | Stream.generate/iterate |
举个例子:
List<String> list = new ArrayList<>();
//从集合创建
list.stream();
list.parallelStream();
//从数组创建
Arrays.stream(new int[]{2,3,4});
//创建数字流
IntStream.of(1,2,3);
//创建是1-10的流
IntStream.rangeClosed(1 , 10);
//random创建无限流,所以需要用limit做限制
new Random().ints().limit(10);
//自定义
Random random = new Random();
Stream.generate(() -> random.nextInt()).limit(20);
中间操作
相关方法 | |
无状态操作 | map / mapToXxx flatMap / flatMapToXxx filter peek unordered |
有状态操作 | distinct sorted limit / skip |
无状态指当前操作跟其他元素的前后没有依赖关系。
有状态就是有关系。
关于flatMap,比如A元素下有B(是个集合)属性,最终得到所有A元素中的所有B属性
public class StreamDemo {
public static void main(String[] args) {
String str = "my name is haozirou";
//map
Stream.of(str.split(" "))
.map(s -> s.length())
.forEach(System.out::println);
//flatMap
Stream.of(str.split(" "))
.flatMap(s -> s.chars().boxed())
.forEach(c -> System.out.print((char)c.intValue() + " "));
}
}
结果:
需要注意的是,如果不加boxed,会报错
这是因为intStream/longStream并不是Stream的子类,所以需要进行装箱boxed
源码:
终止操作
方法 | |
非短路操作 | forEach / forEachOrdered collect / toArray reduce min / max / count |
短路操作 | findFirst / findAny allMatch / anyMatch / noneMatch |
短路操作是指不需要等待这个流所有都操作完,只要满足条件就可以结束。
public class StreamDemo {
public static void main(String[] args) {
String str = "my name is haozirou";
//并行流
str.chars().parallel()
.forEach(i -> System.out.print((char)i));
System.out.println();
str.chars().parallel()
.forEachOrdered(i -> System.out.print((char)i));
}
}
结果:
可以看出,并行的情况下,forEach并不能保证顺序。
reduce可以进行字符串拼接
public class StreamDemo {
public static void main(String[] args) {
String str = "my name is haozirou";
Optional<String> reduce = Stream.of(str.split(" "))
.reduce((s1, s2) -> s1 + "|" + s2);
System.out.println(reduce.orElse(""));
}
}
结果:
reduce还可以提供默认值,可以写成:
String reduce = Stream.of(str.split(" "))
.reduce("", (s1, s2) -> s1 + "|" + s2);
并行流和串行流
并行(parallel):
public class StreamDemo {
public static void main(String[] args) {
//调用parallel 产生一个并行流
IntStream.range(1 , 100).parallel()
.forEach(StreamDemo::debug);
}
public static void debug(int i){
System.out.println(Thread.currentThread().getName() + ":debug" + i);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
串行(sequential):
结果会一个一个打印
需要注意的是,如果多次调用并行和串行的情况下,会默认以最后一次调用为准。
从上面可以看到,并行流使用的线程池是:ForkJoinPool.commonPool,默认线程数和cup个数一样。如何修改默认线程数?
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism" , "2");
结果:
如何使用自己的线程池?
public class StreamDemo {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool(5);
pool.submit(() -> IntStream.range(1 , 10).parallel()
.forEach(StreamDemo::debug));
pool.shutdown();
synchronized (pool){
try {
pool.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void debug(int i){
System.out.println(Thread.currentThread().getName() + ":debug" + i);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
收集器
最常用的有
.collect(Collectors.toList());
如果想指定集合类型
.collect(Collectors.toCollection(TreeSet::new));
假如有一堆学生,需要得到学生的年龄汇总:
IntSummaryStatistics ages = students.stream()
.collect(Collectors.summarizingInt(Student::getAge));
System.out.println("年龄汇总信息" + ages);
结果:
分块
Map<Boolean , List<Student>> genders = students.stream()
.collect(Collectors.partitioningBy(s -> s.getGender() == Gender.MALE));
MapUtils.verbosePrint(System.out , "男女学生列表" , genders);
结果:
分组:
Map<Grade , List<Student> grades= students.stream()
.collect(Collectors.groupingBy(Student::getGrade));
MapUtils.verbosePrint(System.out , "班级学生列表" , grades);
结果:
在分组基础上,获得所有班级学生的个数:
Map<Grade , Long> gradeCount= students.stream()
.collect(Collectors.groupingBy(Student::getGrade , Collectors.counting()));
MapUtils.verbosePrint(System.out , "班级学生个数列表" , gradeCount);
结果:
运行机制
1.所有操作都是链式调用,一个元素只迭代一次。
2.每一个中间操作都会返回一个新的流。
3.无状态,有状态,无状态穿插的时候,先是前两个无状态和有状态交替执行,全部执行完以后,才会执行最后的无状态操作。也就是说有状态和之前全部执行完,才会继续往下进行。
4.并行环境下,有状态的中间操作不一定能并行。
5.并行和串行 也是中间操作(返回stream),但他们不创建流,只修改sourceStage中的parallel标志。