Java8中提供了java.util.stream
包,使用这个包可以进行流式操作(stream)。流式操作是Java语言非常具有革命性的改变,它使得在数组及集合上进行函数式
操作(如过滤、排序、汇总等)成为可能,而且这些操作可以并行的进行。函数式操作、并行运算这两个特点是适应了计算领域的最新发展。
使用流的基本方法
流是指能够进行串行或并列函数式操作的一系列元素。使用流要经过两个步骤,首先你需要获得流,然后就是操作流。
你可以在数组或者集合上获得流,有以下4种情况:
- 在数组上获得流:
Arrays.stream()
- 在数组上获得并行处理流:
Arrays.stream.parallel()
- 在集合(Collection,包括Set,List)上获得流:
collection.stream()
- 在集合上获得并行流:
collection.parallelStream()
获得的流对象是已实现java.util.stream.Stream
接口的对象,其上可以进行一系列的操作,包括过滤、求和等等。
可以看一个例子:
import java.util.Arrays;
import java.util.OptionalInt;
public class streamExcise {
public static void main(String[] args) {
int[] array = new int[100];
for (int i=0;i<array.length;i++)
array[i] = i;
OptionalInt result = Arrays.stream(array).parallel()
.filter(i->i>20) // 过滤,条件是大于20
.map(i->i*i) // 映射,得到数组中每个数的平方
.sorted() // 排序
.distinct() // 去掉重复元素
.limit(10) // 取其中前10个元素
.max(); // 求出当前数组的最大值
System.out.println(result.isPresent()?"最大值为"+result.getAsInt():"无值");
}
}
其中OptionalInt
是一个可能有值得整数,也可能没有值。通过isPresent()
判断是否有值,使用getAsInt()
得到其中得值。
这段程序输出结果是最大值为900
。
另一个例子,对集合使用流:
public class streamList {
public static void main(String[] args) {
Collection <Person> people = Arrays.asList(
new Person("Ted",18,88),
new Person("Charlotte",18,88),
new Person("Michael",18,99),
new Person("Matthew",19,84),
new Person("Matthew",21,84)
);
OptionalDouble result = people.parallelStream()
.filter(p->p.age>20)
.sorted(Person::better)
.sorted(Comparator.comparing(Person::getName))
.limit(5)
.mapToDouble(p->p.score)
.average();
System.out.println(result);
}
}
class Person {
String name;
int age;
double score;
Person(String name, int age, double score) {
this.name = name;
this.age = age;
this.score= score;
}
public String getName() {
return name;
}
public String toString() {
return String.format("%s[%d](%f)",name,age,score);
}
public static int better(Person p1, Person p2) {
return (int)(p2.score-p1.score);
}
}
流及操作的种类
流的种类
Stream
是普通的流,它的子接口还有以下几种:
IntStream
:整数的流。LongStream
:长整数的流。DoubleStream
:实数的流。
对于普通的Stream
可以通过mapToInt()、mapToLong()、mapToDouble()
来转换成相应的流。
流式操作
流式操作既可以是中间的(intermediate opeartion)
也可以是末端的(terminal operation)
。
中间的流操作保持流为打开状态,并允许后续的操作,当一个末端操作被调用的时候,流会被消耗并且不再可用。需要注意的是,流式操作一定要以末端操作结尾。不然编译器会报异常。
常见中间操作如下:
操作 | 作用 |
---|---|
filter | 按照给定条件过滤元素 |
map | 将元素按照给定条件就进行映射 |
flatMap | 将每个元素转变为无或更多的元素 |
peek | 对每个遇到的元素执行一些操作 |
distinct | 根据.equals行为排除所有重复的元素 |
sorted | 将流中元素排序,确保后续操作按照此顺序进行 |
limit | 保证后续的操作所能看到的最大数量的元素 |
substream | 确保后续的操作只能看到一个范围的元素 |
skip | 忽略一些元素 |
mapToDouble、mapToInt、mapToLong | 类型转换 |
常见的末端操作如下:
操作 | 作用 |
---|---|
forEach | 对流中的每个元素执行一些操作 |
toArray | 对流中的元素倾倒入一个数组 |
min | 根据一个比较器找到流中元素的最小值 |
max | 根据一个比较器找到流中元素的最大值 |
count | 计算流中元素的数量 |
anyMatch | 判断流中是否至少有一个元素都匹配断言 |
allMatch | 判断流中是否每一个元素都匹配断言 |
noneMatch | 判断流中是否没有一个元素匹配断言 |
findFirst | 查找流中的第一个元素 |
findAny | 查找流中的任意元素 |
对于流的子接口如IntStream,LongStream,DoubleStream
还有更多的操作,如sum求和、average求平均值、summaryStatistics进行统计
等。