前言
在讲解Lambda表达式时,就介绍了Lambda表达式可以简化集合和数组的遍历、过滤和提取等操作。JDK8中新增了一个Stream接口,该接口可以将集合、数组中的元素转换成Stream流的形式,并结合Lambda表达式的优势进一步简化集合、数组中的元素的查找、过滤、和转换等操作,这一操作就是聚合操作。
一、Stream流概述
Stream 是 Java8 的新特性,它允许你以声明式的方式处理数据集合,可以把它看作是遍历数据集的高级迭代器。
注意:stream流和之前所学的I/O流没有任何的关系,是两个不同的概念
在程序中,使用Stream流没有绝对的语法规范,根据实际操作流程,主要分为以下三个步骤:
1.将原始集合或数组对象转换为Stream流对象
2.对Stream流对象中的元素进行一系列的过滤、查找等中间操作,仍然返回一个Stream流对象
3.对Stream流进行遍历、统计、收集等终结操作,获取想要的结果。
public class Example2 {
public static void main(String[] args) {
//创建一个List集合对象
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("张小白");
list.add("小黑");
//1.创建一个Stream流对象
Stream<String> stream = list.stream();
//2.对Stream流中的元素分别进行过滤、截取操作(中间操作)
Stream<String> stream2 = stream.filter(i -> i.startsWith("张"));
Stream<String> stream3 = stream2.limit(2);
//3.对Stream流中的元素进行终结操作,进行遍历输出
stream3.forEach(j -> System.out.println(j));
System.out.println("-----------");
//通过链式表达式的进行完成聚合操作,比上述方式简便
list.stream().filter(i -> i.startsWith("张"))
.limit(2)
.forEach(i -> System.out.println(i));
}
}
这里使用的就是 Java8 中的 stream 流,使用的是声明性方式写的:说明想要完成什么(过滤,截取,遍历),而不说明如何实现一个操作(for 循环)。
同时可以看出使用链式表达式的语法格式更加简洁、高效,这种链式调用也称为操作管道流
二、创建Stream流对象
聚合操作针对的就是可迭代数据进行的操作,如集合、数组等,所以创建Stream流对象其实就是将集合、数组等通过一些方法转换成Stream流对象。
1.使用 Collection 接口下的 stream()
List<Integer> list = new ArrayList<>();
Stream stream = list.stream();
2.使用 Arrays 中的 stream() 方法,将数组转成流
Integer[] arr1 = new Integer[10];
Arrays.stream(arr1);
3.使用 Stream 中的静态方法:of()
Integer[]array=new Integer[]{1,2,3,4,5,6};
Stream.of(array);
三、Stream流常用的方法
JDK8为聚合操作中的Stream流对象提供了非常丰富的操作方法,这些方法被划分为中间操作(过滤、截取、排序)和终结操作(遍历、统计、收集)两种类型。这两种类型操作方法的根本区别就是方法的返回值,只要返回值类型不是Stream类型的就是终结操作,将会终结当前流模型,而其他的操作都属于中间操作。
下面对Stream的几个常用方法进行演示和说明
1.遍历
public class Example3 {
public static void main(String[] args) {
//通过字符串数据创建了一个Stream流对象
Stream<String> stream = Stream.of("张三", "李四", "张小明", "张阳");
//通过forEach方法遍历Stream流对象中的元素
stream.forEach((i)->{ System.out.println(i);});
/*另外一种写法
stream.forEach(System.out::println);*/
}
}
2.排序、去重、过滤
Integer[] arr = new Integer[]{1,4,3,6,7,9,2,7};
System.out.println("----------过滤,排序,去重,-------------");
//过滤掉小于等于4的数据,去掉重复数据,并且进行升序排序
Arrays.stream(arr).filter((e)->{return e>4;}).sorted().distinct().forEach((t)->{
System.out.println(t);
});
3.映射
map()方法可以将流对象中的元素通过特定的规则进行修改然后映射成另外一个流对象
public class Example4 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("a1", "a2", "b1", "c2", "c1");
stream.filter((s)->{ return s.startsWith("c");}) //筛选出流中以c开头的元素
.map(String::toUpperCase) //对流元素进行映射,全部字符改成大写
.sorted() //对流元素进行排序
.forEach(System.out::println);//对流元素进行遍历输出
}
}
4.跳过、截取(实现分页)
Integer[] arr = new Integer[]{1,4,3,6,7,9,2,7};
System.out.println("-------跳过,截取(实现分页)---------------");
System.out.println("-------第一页-------");
Arrays.stream(arr).skip(0).limit(2).forEach((t)->{
System.out.println(t);
});
System.out.println("-------第二页-------");
Arrays.stream(arr).skip(2).limit(2).forEach((t)->{
System.out.println(t);
});
System.out.println("-------第三页-------");
Arrays.stream(arr).skip(4).limit(2).forEach((t)->{
System.out.println(t);
});
System.out.println("-------第四页-------");
Arrays.stream(arr).skip(6).limit(2).forEach((t)->{
System.out.println(t);
});
5.收集
收集操作是一种十分有用的终结操作,它可以把Stream中的元素保存为另外一种形式,比如集合、字符串等。
public class Example5 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张三", "李四", "张小明", "张阳");
//通过filter()方法筛选出字符串中以张开头的元素
//最后通过collect()方法进行终结操作,将流元素收集到一个List集合中
List<String> list = stream.filter(i -> i.startsWith("张")).collect(Collectors.toList());
System.out.println(list);
//通过filter()方法筛选出字符串中以张开头的元素
//最后通过collect()方法进行终结操作,将流元素使用amd连接收集到一个字符串中
Stream<String> stream2 = Stream.of("张三", "李四", "张小明", "张阳");
String s = stream2.filter(i -> i.startsWith("张")).collect(Collectors.joining("and"));
System.out.println(s);
}
}
6.其他一些操作
Integer[] arr = new Integer[]{1,4,3,6,7,9,2,7};
System.out.println("-------终端操作最小值-------");
System.out.println(Arrays.stream(arr).min((t1,t2)->{return t1-t2; }).get());
System.out.println("-------终端操作元素个数-------");
System.out.println(Arrays.stream(arr).count());
System.out.println("-------终端操作元素求和-------");
System.out.println(Arrays.stream(arr).reduce(0, (a, b) -> a + b));
System.out.println("-------终端操作元素anyMatch-------");
System.out.println(Arrays.stream(arr).anyMatch((e)->{return e>4;}));
List<Apple>appleList=new ArrayList<>();
appleList.add(new Apple("小红",10,"red"));
appleList.add(new Apple("小白",11,"white"));
appleList.add(new Apple("小绿",12,"green"));
System.out.println("-------中间操作map-------");
/*map(): 将其映射成一个新的元素*/
Set set= appleList.stream().map(Apple::getName).collect(Collectors.toSet());
System.out.println(set);
总结
在开发中,多数情况下会涉及对集合、数组中的元素的操作,在JDK8之前都是通过普通的循环遍历出每一个元素,然后还会穿插一些if语句选择性地对元素进行查找、过滤、转换等操作,这种原始的操作方法代码量较大且执行效率低。通过本章的学习,我们可以使用Stream流并且结合Lambda表达式进一步简化这些操作,提高我们的开发效率。