书接上文
热身运动
找出该流中大于2的元素,然后将每个元素乘以2,然后忽略掉流中的前两个元素,然后再取流中的前两个元素,最后求出流中元素的总和。
- 代码
public class StreamTest8 {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1,2,3,5,7,8);
stream
.filter(item -> item > 2)// 过滤不大于2的元素 , 3,5,7,8
.mapToInt(item -> item * 2)//每个元素乘2 , 6,10,14,16
.skip(2)//忽略前两个元素 14,16
.limit(2) //取前两个元素 14,16
.reduce(Integer::sum) // 求和 14+16 = 30 ,返回optional
.ifPresent(System.out::println); //如果Optional不空就输出
}
}
将流中字符串的首字母大写
public class StreamTest7 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("aa","bb","cc","dd ee");
stream.map(item -> item.substring(0,1).toUpperCase(Locale.ROOT)+item.substring(1))
.forEach(System.out::println);
}
}
求最大最小值,总和以及平均值(
IntSummaryStatistics的使用)
public class StreamTest7 {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(2,4,6,8,10,15);
//需要将Integer拆箱成int
IntSummaryStatistics summaryStatistics =stream.mapToInt(Integer::valueOf).summaryStatistics();
System.out.println(summaryStatistics);
}
}
关于Stream的注意点
- 何谓中间操作和终止操作?(点开Stream类的源码看看方法文档就可以知道是什么操作)
中间操作
终止操作
1、流不会改变底层的数据
2、没有终止操作stream的中间操作是不会执行的
- 惰性求值: 没有终止操作,中间操作就不会执行
- 及早求值: 一旦出现终止操作,立马执行中间操作
3、中间操作的返回值一般是一个新的Stream对象
- 不被推荐但是正确的写法(本例只用来说明中间操作会返回新的Stream)
public class StreamTest7 {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(2,4,6,8,10,15);
Stream<Integer> stream1 = stream.map(item ->item*2);
Stream<Integer> stream2 = stream1.filter(item -> item%2==0);
Stream<Integer> stream3 = stream2.sorted((a,b)-> b-a);
//Optional optional = stream3.max((a,b)->b-a);
stream3.forEach(item -> System.out.print(item+" "));
System.out.println("\nstream:"+stream+", stream1:"+stream1+", stream2:"+stream2);
//应该用链式写法
}
}
4、一个流一旦挂载了中间操作或终止操作就不能再继续使用
加深一下对Stream的理解
以sql语句来体验一下Stream
- 以查询为例
select name from student where age > 20 and address = "上海" order by age desc
# 查询地址在上海年龄大于20 的学生名字 将其按年龄升序排
- 在不用Stream时,用Java实现上面sql一样的功能
List<Student> list = new ArrayList<>();
for(Student stu : students){//这里还省略了students这么来的呢,是不是很麻烦
if(stu.getAge() > 20 && stu.getAddress().equals("上海")){
list.add(stu);
}
}
Collections.sort(list,Comparetor()...);
for(Student stu : list ){
System.out.println(stu.getName());
}
- 使用Stream
students
.stream()
.filter(stu -> stu.getAge() > 20 && stu.getAddress().equals("上海"))
.sort(...)
.forEach(stu -> System.out.print(stu.gatName()+" ");
Stream一气呵成,几乎和sql形状类似
6、 流是短路操作
- 何谓短路操作?
以Java谓词为例:
- A
&&B 当A为假时B不会执行- C
||D 当C为真时D不会执行
//找出第一个String长度为2,如果存在就输出 2
public class StreamTest9 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("aa","bb","bbbdd");
stream.map(item ->{//将String元素变成对应长度 int
int len = item.length();
System.out.println(item+" in map");
return len;
}).filter(item -> item==2).findFirst().ifPresent(System.out::println);
}
}
map并不是遍历,只要找到符合条件的,流就结束了
进阶使用
并行流
和串行流的使用方法一样
public class ParallelStream {
public static void main(String[] args) {
Stream<String> stream =Stream.generate(UUID.randomUUID()::toString).limit(5000000);//提取固定个数的UUID
List<String> list = stream.collect(Collectors.toList());//将5000000个UUID放入list
long a = System.nanoTime();
list.parallelStream().sorted().parallel().forEach(l ->System.out.print(""));
long b = System.nanoTime();
System.out.println("并行运行时间:"+TimeUnit.NANOSECONDS.toMillis(b-a));//排序和forEach的运行时间
System.out.println("----------------");
a = System.nanoTime();
list.stream().sorted().forEach(l ->System.out.print(""));
b = System.nanoTime();
System.out.println("串行运行时间:"+TimeUnit.NANOSECONDS.toMillis(b-a));
}
}
有时候并行不一定比串行快
flatMap的使用
- 提取单词并去重
public class FlatMap {
public static void main(String[] args) {
Stream<String> stream = Stream.of("an apple","Make me crazy, but it’s fine","Make fine");
stream
.map(item -> item.split(" "))//返回Stream<String[]>
.flatMap(Arrays::stream)//多个Stream.of(String[]),汇聚成一个Stream
.distinct()//去重
.collect(Collectors.toList())//变成List
.forEach(System.out::println);//输出
}
}
所以
map返回Stream<String[]>
===================================
- 将list中的一个元素分别拼接到另外一个集合的每个元素上
public class FlatMap {
public static void main(String[] args) {
List<String> list1 = Arrays.asList("Hi~ ","Hello~ ","你好~ ");
List<String> list2 = Arrays.asList("王老板 ","赵老板 ","李老板 ");
list1.stream() //拼接
.flatMap(item -> list2.stream().map(item2 -> item + item2 ))//流中流
.collect(Collectors.toList())
.forEach(System.out::println);
}
}
分组(Collectors.groupingBy)
按名字分组
public class GroupBy {
public static void main(String[] args) {
Student student1 = new Student("老王",80,13);
Student student2 = new Student("老李",60,20);
Student student3 = new Student("老赵",55,28);
Student student4 = new Student("老王",90,10);
Stream<Student> stream =Stream.of(student1,student2,student3,student4);
Map<String, List<Student>> map = stream.collect(Collectors.groupingBy(Student::getName));
map.forEach((key, value) -> {
System.out.println(key + ":" + value);
});
}
}
分区(Collectors.partitioningBy)
- 分区是分组的特殊化,根据一个条件分成ture区和false区
将及格和不及格分开
public class GroupBy {
public static void main(String[] args) {
Student student1 = new Student("老王",80,13);
Student student2 = new Student("老李",60,20);
Student student3 = new Student("老赵",55,28);
Student student4 = new Student("老王",54,10);
Stream<Student> stream =Stream.of(student1,student2,student3,student4);
Map<Boolean, List<Student>> map = stream.collect(Collectors.partitioningBy(stu -> stu.getScore()>=60));
map.forEach((key, value) -> {
System.out.println(key + ":" + value);
});
}
}