简介:
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
stram和parallelStream(顺序流和并行流)
stream和parallelStream的简单区分: stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:
如果流中的数据量足够大,并行流可以加快处速度。
除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:
Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();
使用:
Stream的创建
1,使用Collection下的 stream() 和 parallelStream() 方法
List<String> list = new ArrayList<String>();
Stream<String> stream = list.stream();//获取一个顺序流
Stream<String> parallelStream = list.parallelStream();//获取一个并行流
2,使用Array中的stream()方法,将数组转成流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
3,使用Stream中的静态方法:of(),iterate(),generate()
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);
4,使用BufferedReader.lines()方法,将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
5,使用Pattern.spliAsStream()方法,将字符串分隔成流
Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
流的操作
Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
1,遍历/匹配(foreach/find/match)
Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在
List<Integer> list = Arrays.asList(3,6,13,5,8,10,7,2,1);
//遍历输出符合条件的元素
list.stream().filter(x -> x > 3).forEach(System.out::println);
//匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 3).findFirst();
//匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 3).findAny();
//是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(x -> x > 3);
System.out.println("匹配第一个值:" + findFirst.get());
System.out.println("匹配任意一个值:" + findAny.get());
System.out.println("是否存在大于6的值:" + anyMatch);
结果为:
2,筛选(filter)
filter 方法用于通过设置的条件过滤出元素
List<Integer> list = Arrays.asList(3,6,13,5,8,10,7,2,1);
//获取比5大的数
list.stream().filter(x -> x > 5).forEach(System.out::println);
结果为:
3,映射(map/flatMap)
映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:
- map: 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成新的元素
- flatMap: 接收一个函数作为参数,将流中的每一值都换成另一个流,然后把所有流连接成一个流
List<String> list = Arrays.asList("a,b,c","1,2,3");
//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replace(",",""));
s1.forEach(System.out::println);
Stream<String> s3 = list.stream().flatMap(s -> {
//将每个元素转换成一个stream
String[] split = s.split(",");
Stream<String> s2 = Arrays.stream(split);
return s2;
});
System.out.println("--------------------");
s3.forEach(System.out::println);
结果为
4,聚合(max/min/count)
- count: 返回流中元素的总个数
- min: 返回流中元素的最小值
- max: 返回流中元素的最大值
public class Test {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
long count = list.stream().count();
Integer max = list.stream().max(Integer::compareTo).get();
Integer min = list.stream().min(Integer::compareTo).get();
System.out.println("元素的总个数:"+count);
System.out.println("元素中最大值:"+max);
System.out.println("元素中最小值:"+min);
}
}
结果为
5,归约(reduce)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和,求乘积和求最值操作
public class Test {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
//求和方式1
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
//求和方式2
Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
//求和方式3
Integer sum3 = list.stream().reduce(0, Integer::sum);
//求乘积
Optional<Integer> product = list.stream().reduce((x,y) -> x * y);
//求最大值方式1
Optional<Integer> max = list.stream().reduce((x,y) -> x > y ? x : y);
//求最大值方式2
Integer max2 = list.stream().reduce(1,Integer::max);
System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
System.out.println("list求积:" + product.get());
System.out.println("list求最大值:" + max.get() + "," + max2);
}
}
结果为:
6,排序(sorted)
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):定制排序,自定义Comparator排序器
student类
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
List<String> list = Arrays.asList("aa", "ff", "dd");
//String类自身实现Comparable接口
list.stream().sorted().forEach(System.out::println);
Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
Student s3 = new Student("aa", 30);
Student s4 = new Student("dd", 40);
List<Student> sList = Arrays.asList(s1,s2,s3,s4);
//自定义排序:先按名字升序,名字相同则按年龄升序
sList.stream().sorted(
(o1, o2) -> {
if(o1.getName().equals(o2.getName())){
return o1.getAge() - o2.getAge();
}else{
return o1.getName().compareTo(o2.getName());
}
}
).forEach(System.out::println);
}
}
结果为:
7,收集(collect)
collect主要依赖java.util.stream.Collectors类内置的静态方法。
7.1 归集(toList/toSet/toMap)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法
public class Test {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
System.out.println("toList:" + listNew);
System.out.println("toSet:" + set);
}
}
结果为:
7.2统计(count/averaging)
Collectors提供了一系列用于数据统计的静态方法:
- 计数:count
- 平均值:averagingInt、averagingLong、averagingDouble
- 最值:maxBy、minBy
- 求和:summingInt、summingLong、summingDouble
- 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
Person类
public class Person {
private String name;
private double salary;
public Person(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
public class Test {
public static void main(String[] args) {
List<Person> pList = new ArrayList<>();
pList.add(new Person("Tom",7000));
pList.add(new Person("Jack",9000));
pList.add(new Person("Jerry",6500));
//求总和
long count = pList.stream().collect(Collectors.counting());
//求平均工资
Double average = pList.stream().collect(Collectors.averagingDouble(Person::getSalary));
//求最高工资
Optional<Double> max = pList.stream().map(Person::getSalary).collect(Collectors.maxBy(Double::compare));
//求工资之和
Double sum = pList.stream().collect(Collectors.summingDouble(Person::getSalary));
//一次性统计所有信
DoubleSummaryStatistics collect = pList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
System.out.println("员工总数:" + count);
System.out.println("员工平均工资:" + average);
System.out.println("员工最高工资:"+ max);
System.out.println("员工工资总和:" + sum);
System.out.println("员工工资所有统计:" + collect);
}
}
结果为:
7.3分组(partitioningBy/groupingBy)
- 分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
- 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
public class Test {
public static void main(String[] args) {
List<Person> pList = new ArrayList<>();
pList.add(new Person("Tom",7000));
pList.add(new Person("Jack",9000));
pList.add(new Person("Jerry",6500));
pList.add(new Person("Anni",8000));
pList.add(new Person("Owen",10000));
pList.add(new Person("Alisa",7900));
//将员工按薪资是否高于8000分组
Map<Boolean, List<Person>> part = pList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
System.out.println("员工按薪资是否大于8000分组情况:" + part);
}
}
结果为:
员工按薪资是否大于8000分组情况:{false=[Person{name='Tom', salary=7000.0}, Person{name='Jerry', salary=6500.0}, Person{name='Anni', salary=8000.0}, Person{name='Alisa', salary=7900.0}], true=[Person{name='Jack', salary=9000.0}, Person{name='Owen', salary=10000.0}]}
7.4 接合(joining)
joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
public class Test {
public static void main(String[] args) {
List<Person> pList = new ArrayList<>();
pList.add(new Person("Tom",7000));
pList.add(new Person("Jack",9000));
pList.add(new Person("Jerry",6500));
pList.add(new Person("Anni",8000));
pList.add(new Person("Owen",10000));
pList.add(new Person("Alisa",7900));
String names = pList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
System.out.println("所有员工的姓名:" + names);
List<String> list = Arrays.asList("A", "B", "C");
String string = list.stream().collect(Collectors.joining("-"));
System.out.println("拼接后的字符串:" + string);
}
}
结果为:
7.5 归约(reducing)
Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持
public class Test {
public static void main(String[] args) {
List<Person> pList = new ArrayList<>();
pList.add(new Person("Tom",7000));
pList.add(new Person("Jack",9000));
pList.add(new Person("Jerry",6500));
pList.add(new Person("Anni",8000));
pList.add(new Person("Owen",10000));
pList.add(new Person("Alisa",7900));
//员工工资总和
Double sum = pList.stream().map(Person::getSalary).collect(Collectors.reducing(Double::sum)).get();
System.out.println("员工工资总和:"+sum);
}
}
结果为:
8.提取/组合
流也可以进行合并、去重、限制、跳过等操作。
public class Test {
public static void main(String[] args) {
String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "d", "e", "f", "g" };
Stream<String> s1 = Stream.of(arr1);
Stream<String> s2 = Stream.of(arr2);
//concat:合并两个流 distinct:去重
List<String> newList = Stream.concat(s1, s2).distinct().collect(Collectors.toList());
//limit:限制从流中获得前n个数据
List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
//skip:跳过前n个数据
List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
System.out.println("流合并:" + newList);
System.out.println("limit:" + collect);
System.out.println("skip:" + collect2);
}
}
结果为: