Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。是函数式编程的特性。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
就像工厂流水线一样。
我们就可以把一个Stream当做流水线处理:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
//移除为B的元素
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
if(iterator.next().equals("B")) iterator.remove();
}
//Stream操作
list = list //链式调用
.stream() //获取流
.filter(e -> !e.equals("B")) //只允许所有不是B的元素通过流水线
.collect(Collectors.toList()); //将流水线中的元素重新收集起来,变回List
System.out.println(list);
}
对一个数组去重并进行从大到小排列, 再把每个元素+1, 最后只获取前两个元素:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(3);
list = list
.stream()
.distinct() //去重(使用equals判断)
.sorted((a, b) -> b - a) //进行倒序排列
.map(e -> e+1) //每个元素都要执行+1操作
.limit(2) //只放行前两个元素
.collect(Collectors.toList());
System.out.println(list);
}
当遇到大量的复杂操作时,我们就可以使用Stream来快速编写代码,这样不仅代码量大幅度减少,而且逻辑也更加清晰明了(如果你学习过SQL的话,你会发现它更像一个Sql语句)
执行顺序
Stream流的执行顺序可以分为中间操作和终端操作两个阶段。
- 中间操作:中间操作是对流进行转换、过滤、映射等操作,可以链式调用多个中间操作。这些操作不会立即执行,而是在遇到终端操作时才会触发执行。常见的中间操作有filter、map、sorted、distinct等。
- 终端操作:终端操作是对流进行最终的处理,会触发中间操作的执行并产生最终的结果。终端操作是流的最后一个操作,一旦执行完毕,流就不能再被使用。常见的终端操作有forEach、collect、reduce、count等。
在执行过程中,中间操作会按照链式调用的顺序依次执行,每个中间操作的结果会作为下一个中间操作的输入。终端操作会触发中间操作的执行,并产生最终的结果。
需要注意的是,Stream流的操作是惰性求值的,即只有在终端操作触发时才会执行中间操作。这种方式可以提高效率,避免不必要的计算。
总结起来,Java Stream流的执行顺序是:中间操作按照链式调用的顺序依次执行,终端操作触发中间操作的执行并产生最终结果。
应用
取一堆(-100, 100)随机数, 只求前10个,且保留小于0的数,从大到小排序并打印:
public static void main(String[] args) {
Random random = new Random(); //没想到吧,Random支持直接生成随机数的流
random
.ints(-100, 100) //生成-100~100之间的,随机int型数字(本质上是一个IntStream)
.limit(10) //只获取前10个数字(这是一个无限制的流,如果不加以限制,将会无限进行下去!)
.filter(i -> i < 0) //只保留小于0的数字
.sorted() //默认从小到大排序
.forEach(System.out::println); //依次打印
}
生成一个统计实例来帮助我们快速进行统计:
public static void main(String[] args) {
Random random = new Random(); //Random是一个随机数工具类
IntSummaryStatistics statistics = random
.ints(0, 100)
.limit(100)
.summaryStatistics(); //获取语法统计实例
System.out.println(statistics.getMax()); //快速获取最大值
System.out.println(statistics.getCount()); //获取数量
System.out.println(statistics.getAverage()); //获取平均值
}
对一个含有 “A,B” 的列表, 按照 ”,” 进行元素分割出新的列表:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A,B");
list.add("C,D");
list.add("E,F"); //我们想让每一个元素通过,进行分割,变成独立的6个元素
list = list
.stream() //生成流
.flatMap(e -> Arrays.stream(e.split(","))) //分割字符串并生成新的流
.collect(Collectors.toList()); //汇成新的List
System.out.println(list); //得到结果
}
List计算所有数字的和:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
int sum = list
.stream()
.reduce((a, b) -> a + b) //计算规则为:a是上一次计算的值,b是当前要计算的参数,这里是求和
.get(); //我们发现得到的是一个Optional类实例,通过get方法返回得到的值
System.out.println(sum);
}