pipeline (管道)的概念
一个数据操作的流程,例如 转换,聚合,统计 ,遍历 等一系列操作构成一个数据通道,通道中的数据流就会被管道转换,聚合,统计 ,遍历。
pipeline的组成部分
数据源 :数据集,可以是I/O流,数组或集合等
中间操作:转换,聚合,统计 ,遍历 等
终结操作:对数据的最后操作,例如forEach等,执行终结操作,表明管道中的数据离开管道
stream的概念
stream是一组元素的序列,并不像传统的集合那样只保存元素,它携带数据通过管道,是数据的载体。
例子:
roster的类型是List<Person>
roster
.stream()//转换成流
.filter(e -> e.getGender() == Person.Sex.MALE)//过滤 中间操作
.forEach(e -> System.out.println(e.getName()));//遍历 终结操作
上面的一系列操作构成一个管道,如果仅仅是把数据源换了下面的操作不做更改,那么此 管道仍然可用。
一句话:管道相当于操作序列,流相当于数据载体,流通过管道会被管道的结构所影响
java.util.stream包
java.util.stream下的包提供了对元素流的函数式操作,例如对集合中的元素做转换,聚合,统计 ,遍历 等操作。
下面通过例子来学习关于流操作的用法,在这之前需要先定义一个Person类,用于数据的获取和之后的例子中
package com.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
public class Person {
private int age;
private String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static List<Person> getListPerson(){
List<Person> list=new ArrayList<>();
for(int i=0;i<100;i++) {
list.add(new Person(new Random().nextInt(30),UUID.randomUUID().toString().split("-")[0]));
}
return list;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
下面是具体的操作,包括过滤,转换,聚合,分组,多条件分组等操作:
package com.stream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.model.*;
public class StreamTest {
public static void main(String[] args) {
List<Person> list=Person.getListPerson();
System.out.println("过滤");
List<Person> list1=list.stream().filter(p->p.getName().startsWith("a")).collect(Collectors.toList());
System.out.println(list1);
System.out.println("转换");
List<String> list2=list.stream().map((p)->p.getName()).collect(Collectors.toList());
System.out.println(list2);
System.out.println("聚合 求和1");
double sum=list.stream().mapToInt(p->p.getAge()).sum();
System.out.println(sum);
System.out.println("聚合 平均值1");
double average=list.stream().mapToInt(p->p.getAge()).average().getAsDouble();
System.out.println(average);
System.out.println("聚合 求和2");
double sum2=list.stream().map(p->p.getAge()).reduce(0, (a,b)->{return a+b;});
System.out.println(sum2);
System.out.println("聚合 平均值2");
double average2=list.stream().map(Person::getAge).collect(Averager::new, Averager::accept, Averager::combine).average();
System.out.println(average2);
System.out.println("单条件分组");
Map<Integer,List<Person>> groupByAge=list.stream().collect(Collectors.groupingBy(Person::getAge));
for(Map.Entry<Integer, List<Person>> entry:groupByAge.entrySet()) {
System.out.println(entry.getValue());
}
System.out.println("多条件分组");
Map<Integer,Map<String,List<Person>>> groupByAgeAndNameStartWith=list.stream().collect(Collectors.groupingBy(Person::getAge,Collectors.groupingBy(p->p.getName().substring(0, 1))));
for(Map.Entry<Integer,Map<String,List<Person>>> entryLevel1:groupByAgeAndNameStartWith.entrySet()) {
Map<String,List<Person>>groupByAge1=entryLevel1.getValue();
for(Map.Entry<String, List<Person>> entryLevel2:groupByAge1.entrySet()) {
System.out.println(entryLevel1.getKey()+","+entryLevel2.getKey()+entryLevel2.getValue());
}
}
}
}
其中的聚合求平均值用到了一个中间类Averager,用于提供中间操作的保存和迭代连接等操作,求平均值过程中会产生多个中间
Averager,最后调用连接方法将他们连接成一个Averager,该类代码如下:
package com.model;
import java.util.function.IntConsumer;
public class Averager implements IntConsumer
{
private int total = 0;
private int count = 0;
public double average() {
return count > 0 ? ((double) total)/count : 0;
}
public void accept(int i) { total += i; count++; }
public void combine(Averager other) {
total += other.total;
count += other.count;
}
}