一、Stream介绍
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对 集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数 据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
总之:流(Stream)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
二、Stream特点
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
- Stream和迭代器一样,只能遍历一次,如果需要再次使用,可以从原始数据重新再获取一个流。
三、Stream的三个步骤
(一)总体介绍
- 创建 Stream
使用一个数据源(如:集合、数组),获取一个流 - 中间操作
一个中间操作链,对数据源的数据进行处理 - 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
(二)创建流
创建流主要有4中方法,分别如下:
1. 使用集合创建流
在java8中Collection接口被扩展,提供了两个方法返回流,如下:
default Stream stream() : 返回一个顺序流
default Stream parallelStream() : 返回一个并行流
2. 使用数组创建流
Java8 中的 Arrays 的静态方法 stream() 可 以获取数组流,有一些重载形式,能够处理对应基本类型的数组
static <T> Stream<T> stream(T[] array)
: 返回一个流
对于基本类型派生出了下面三个方法
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
另外,Arrays还有一个重载的两个参数的方法:
public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive)
这个方法会对数组截断之后返回一个子数组的流
3. 由值创建流
可以使用静态方法 Stream.of(), 通过显示值 创建一个流。它可以接收任意数量的参数
public static<T> Stream<T> of(T... values)
4. 由函数创建流
- 生成:
public static<T> Stream<T> generate(Supplier<T> s)
生成一个无限长度的Stream,其元素的生成是通过给定的Supplier(这个接口可以看成一个对象的工厂,每次调用返回一个给定类型的对象) - 迭代:
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的
5. 示例
public void createStream() {
//1.通过Collection集合提供的stream()方法获取流
List<String> sList = new ArrayList<>();
Stream<String> stream = sList.stream();
//2.通过Arrays.stream(数组)获取
String[] array = new String[10];
Stream<String> stream2 = Arrays.stream(array);
//3.通过Stream.of()方法获取
Stream<String> stream3 = Stream.of("Hello");
//4.通过函数产生流
//1).迭代:生成20,21,22,23 ....
Stream<Integer> stream4 = Stream.iterate(20, x -> x + 1);
//2).生成:一直生成100以内的随机数
Stream<Integer> stream5 = Stream.generate(() -> new Random().nextInt(100));
}
(三)中间操作
多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”
中间操作有如下这些:
代码示例:
public class StreamDriver {
List<Employee> elist = Arrays.asList(
new Employee("aa", 45, 8888.88),
new Employee("aa", 45, 8888.88),
new Employee("aa", 45, 8888.88),
new Employee("bb", 32, 6666.66),
new Employee("cc", 47, 7777.77),
new Employee("dd", 89, 5555.55),
new Employee("ee", 12, 9999.99));
@Test
public void middleOp() {
System.out.println("--------查找年龄大于40的人-----");
System.out.println();
Stream<Employee> filter = elist.stream().filter(x -> x.getAge() > 40);
filter.forEach(System.out::println);
System.out.println("-----去重,去掉信息相同的人---------");
Stream<Employee> distinct = elist.stream().distinct();
distinct.forEach(System.out::println);
System.out.println("------跳过2个元素后取2个元素-----------");
elist.stream().skip(2).limit(2).forEach(System.out::println);
System.out.println("------打印每个人的姓名--------------");
elist.stream().map((x) -> x.getName()).forEach(System.out::println);
System.out.println("-----打印每个人的姓名每个字符为一行-----------");
System.out.println("--------1.使用map");
//获取到的是一个流的流
Stream<Stream<Character>> map = elist.stream().map(x -> getCharStram(x.getName()));
map.forEach(x -> x.forEach(System.out::println));
//获取到的是一个合并后的流
System.out.println("--------2.使用flatMap");
Stream<Character> flatMap = elist.stream().flatMap(x -> getCharStram(x.getName()));
flatMap.forEach(System.out::println);
System.out.println("------按年龄排序------");
Stream<Employee> sorted = elist.stream().sorted((x, y) -> x.getAge().compareTo(y.getAge()));
sorted.forEach(System.out::println);
}
/**
* 通过一个字符串获取Stream<Character>
* @param str
* @return
*/
private Stream<Character> getCharStram(String str) {
List<Character> cList = new ArrayList<>();
for (char c : str.toCharArray()) {
cList.add(c);
}
return cList.stream();
}
}
结果如下:
--------查找年龄大于40的人-----
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=cc, age=47, salary=7777.77]
Employee [name=dd, age=89, salary=5555.55]
-----去重,去掉信息相同的人---------
Employee [name=aa, age=45, salary=8888.88]
Employee [name=bb, age=32, salary=6666.66]
Employee [name=cc, age=47, salary=7777.77]
Employee [name=dd, age=89, salary=5555.55]
Employee [name=ee, age=12, salary=9999.99]
------跳过2个元素后取2个元素-----------
Employee [name=aa, age=45, salary=8888.88]
Employee [name=bb, age=32, salary=6666.66]
------打印每个人的姓名--------------
aa
aa
aa
bb
cc
dd
ee
-----打印每个人的姓名每个字符为一行-----------
--------1.使用map
a
a
a
a
a
a
b
b
c
c
d
d
e
e
--------2.使用flatMap
a
a
a
a
a
a
b
b
c
c
d
d
e
e
------按年龄排序------
Employee [name=ee, age=12, salary=9999.99]
Employee [name=bb, age=32, salary=6666.66]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=cc, age=47, salary=7777.77]
Employee [name=dd, age=89, salary=5555.55]
(四)终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。
相关方法如下:
Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态 。
示例代码
/**
* 终止操作
*/
@Test
public void finalOp() {
//检查是否所有的人年龄大于30
boolean allMatch = elist.stream().allMatch(x -> x.getAge() > 30);
System.out.println("是否所有的人年龄大于30 : " + allMatch);
boolean anyMatch = elist.stream().anyMatch(x -> x.getAge() > 30);
System.out.println("查看是否有年龄大于30的:" + anyMatch);
Optional<Employee> first = elist.stream().findFirst();
Employee employee = first.get();
System.out.println("第一个元素是:" + employee);
Optional<Employee> max = elist.stream().max((x, y) -> x.getSalary().compareTo(y.getSalary()));
System.out.println("最高工资是:" + max.get().getSalary());
Optional<Employee> reduce = elist.stream().reduce((x, y) -> {
double s = x.getSalary() + y.getSalary();
x.setSalary(s);
return x;
});
System.out.println("工资总和为:" + reduce.get().getSalary());
}
/**
* 收集
*/
@Test
public void collect() {
//找出集合中年龄大于30岁的,放入到一个新的集合
List<Employee> elist2 = elist.stream().filter(x -> x.getAge() > 30).collect(Collectors.toList());
//统计工资大于7000的人数
Long c = elist.stream().filter(x -> x.getSalary() > 7000).collect(Collectors.counting());
System.out.println("年龄大于7000的人数为:" + c);
Double avgAge = elist.stream().collect(Collectors.averagingInt(x -> x.getAge()));
System.out.println("平均年龄是:" + avgAge);
String collect = elist.stream().map(x -> x.getName()).collect(Collectors.joining(","));
System.out.println("所有的姓名是:" + collect);
}
结果:
是否所有的人年龄大于30 : false
查看是否有年龄大于30的:true
第一个元素是:Employee [name=aa, age=45, salary=8888.88]
最高工资是:9999.99
工资总和为:56666.61000000001
年龄大于7000的人数为:5
平均年龄是:45.0
所有的姓名是:aa,aa,aa,bb,cc,dd,ee