流式编程
流式编程是对集合的增强,流不是集合的元素,也不是数据存储,更像是迭代器,单向遍历集合中的元素,简化对集合对象的操作(删除、过滤、映射、排序…)
注:流式编程最好的是学习lambda表达式,因为流式操作的参数大多数是函数式接口,使用lambda表达式可以简化操作。
步骤
1.获取数据源,将数据源中的数据读取到流中
2.对流中的数据进行各种各样的处理
3.对流中数据进行整合处理
四大函数式接口
消费型接口
@FunctionalInterface
public interface Consumer<T> {
//只有参数没有返回值
void accept(T var1);
}
供给型接口
@FunctionalInterface
public interface Supplier<T> {
//有返回值没有参数传入
T get();
}
函数式接口
@FunctionalInterface
public interface ToIntFunction<T> {
//有参数有一个int类型的返回值
int applyAsInt(T var1);
}
断定式接口
@FunctionalInterface
public interface Predicate<T> {
//单一参数,返回值为布尔类型的方法
boolean test(T var1);
//...相关default方法删除掉
}
数据源获取
数据源
数据源,流对象中的数据的来源,是集合流式编程的第一步,流所用到的数据源来自集合、数组或者I/O
获取流对象
//1.集合对象
//2.从集合对象获取数据元素
//3.流对象处理数据
//从List集合中获取流对象
private static void listStream() {
List<Integer> list = new ArrayList<>();
Collections.addAll(list,0,1,2,3,4,5,6);
Stream<Integer> stream = list.stream();
System.out.println(stream);
}
//从整数型对象的数据中获取数据元素,创建的流是一个带泛型的流对象Stream<Integer>
public static void arrayDataSource(){
Integer[] array = new Integer[]{0,1,2,3,4,5,7,6};
Stream<Integer> stream = Arrays.stream(array);
System.out.println(stream);
}
//int类型的数组转换成流对象的格式是一个IntStream
public static void arrayList(){
int[] array = new int[]{3,5,6,8,7,9,4};
IntStream stream = Arrays.stream(array);
System.out.println(stream);
}
操作流中的数据
//1.准备数据
private static Stream<Integer> getDataSource() {
//准备一个容器
List<Integer> list = new ArrayList<>();
//向数据源添加数据
Collections.addAll(list,0,2,3,5,4,6,8,7);
//获取集合的stream对象
Stream<Integer> stream = list.stream();
return stream;
}
#### 最终操作
collect操作 —- 最终操作
将流中的数据收集到一起,对一些数据进行处理,将流中的数据存入一个集合中,collect方法的参数是一个Collector接口,该接口不是一个函数式接口,因此可以实现改接口,可以自定义收集规则,一般不需要,可以使用Collections工具类进行操作。
最终操作: 指的是该操作执行后会将流关闭
collect操作示例: 将流中的数据放入集合中,可以放入List集合、Set集合、Map集合中去。
//2.collect操作
public static void collection(){
//获取数据源
Stream<Integer> dataSource = getDataSource();
//collect操作
// List<Integer> collect = dataSource.collect(Collectors.toList());
// Set<Integer> collect = dataSource.collect(Collectors.toSet());
Map<String, Integer> collect = dataSource.collect(Collectors.toMap(Object::toString, i -> i));
System.out.println(collect);
}
reduce操作——最终操作
reduce的参数是一个BinaryOperator底层实现的是一个函数式的接口,采用lambda操作对集合中的元素求和
//reduce操作
public static void reduceUse(){
//获取流对象
Stream<Integer> dataSource = getDataSource();
//2.操作流对象
Integer reduce = dataSource.reduce(Integer::sum).get();
System.out.println(reduce);
}
reduce参数的函数式接口的参数,两个输入参数一个输出参数。
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T var1, U var2);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> var1) {
Objects.requireNonNull(var1);
return (var2, var3) -> {
return var1.apply(this.apply(var2, var3));
};
}
}
count操作—统计流中的元素个数
//count操作
public static void countUse(){
//获取数据
Stream<Integer> dataSource = getDataSource();
//获取流中的数据
long count = dataSource.count();
System.out.println(count);
}
forEach操作
//foreach操作
public static void foreach(){
//1.获取数据
Stream<Integer> dataSource = getDataSource();
//使用foreach函数
dataSource.forEach(System.out::println);
}
forEach中的参数是一个消费性接口,只有传入参数没有返回值。
@FunctionalInterface
public interface Consumer<T> {
void accept(T var1);
default Consumer<T> andThen(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
this.accept(var2);
var1.accept(var2);
};
}
}
max&&min
//max&min
//max 按照比较规则比较流中最大的数据元素
//min 按照比较规则比较流中最小的数据元素
public static void maxAndmin(){
//获取数据源
Stream<Integer> dataSource = getDataSource();
// Integer max = dataSource.max(Integer::compareTo).get();
Integer min = dataSource.min(Integer::compareTo).get();
System.out.println("数据流中的最大值为:"+" " +"数据流中的最小值为:"+min);
}
Matching方法
allmatch:只有当流中的所有元素全部匹配时,返回true
anymatch:只有当流中的任意元素匹配时,返回true
nonematch :只有当流中的所有元素全部不匹配时,返回true
//match方法
public static void matchAll(){
Stream<Integer> dataSource = getDataSource();
//全部匹配,当流中的所有元素全部满足条件时,返回true
// boolean b = dataSource.allMatch(e -> e >= 0);
// System.out.println(b);
//部分匹配
// boolean b = dataSource.anyMatch(e -> e > 9);
// System.out.println(b);
//全部不匹配,返回true
boolean b = dataSource.noneMatch(e -> e > 10);
System.out.println(b);
}
noneMatch传入的参数是一个判定性接口Predicate
@FunctionalInterface
public interface Predicate<T> {
boolean test(T var1);
}
find操作
findFirst:从流中获取一个元素(获取开头的元素)
findAny:从流中获取一个元素(获取任意一个元素)
大多数情况下两个函数的结果是相同的,多线程操作时结果可能不一致
//find操作
//findFIrst:获取流中的一个元素,获取的是流中的第一个元素
//findAny:在单线程操作下获取的元素基本上是流中的首部,并行流中获取的元素可能为首部可能为其他
public static void findData(){
//创建集合并添加元素,获取集合流
List<Integer> dataSource = new ArrayList<>();
Collections.addAll(dataSource,1,2,3,4,5,6,7,8,9);
//使用find操作操作流中的数据
// Integer integer = dataSource.parallelStream().findFirst().get();
// System.out.println(integer);
//stream流的findAny操作获取的是流的第一个元素
//并行流findAny获取的是流中的任一元素
Integer integer = dataSource.parallelStream().findAny().get();
System.out.println(integer);
}
对于以上的部分操作单一的流操作,操作结束后会关闭流,因此使用IntSummaryStatistics获取对象的分析结果,能够将流中的数据操作输出。
public static void testArr(){
int[] arr = new int[]{1,2,3,4,5,6,7,8,9};
IntStream stream = Arrays.stream(arr);
//最终操作,每次操作完都将流关闭,每次执行一行程序
//获取最大值
// System.out.println(stream.max().getAsInt());
//获取最小值
// System.out.println(stream.min().getAsInt());
//获取平均值
// System.out.println(stream.average().getAsDouble());
//求和操作
// System.out.println(stream.sum());
//使用下面的对象可以获取一个流的分析结果,可以将以上所有操作全部记录,不需要每操作一次都关闭流
IntSummaryStatistics intSummaryStatistics = stream.summaryStatistics();
System.out.println(intSummaryStatistics.getMin());
System.out.println(intSummaryStatistics.getMax());
System.out.println(intSummaryStatistics.getSum());
System.out.println(intSummaryStatistics.getAverage());
System.out.println(intSummaryStatistics.getCount());
}
中间操作
自定义类型进行操作,定义一个学生类
public class Student implements Comparable<Student>{
private String name;
private int age;
private int score;
//构造器
public Student(){}
public Student(String name,int age,int score){
this.name = name;
this.age = age;
this.score = score;
}
//get、set方法
public void setName(String name){this.name = name;}
public String getName(){return name;}
public void setAge(int age){this.age = age;}
public int getAge(){return age;}
public void setScore(int score){this.score = score;}
public int getScore(){return score;}
//toString方法
@Override
public String toString() {
return "name = "+name+",age = "+age+",score "+score;
}
//重写比较方法
@Override
public int hashCode() {
return Objects.hash(name,age,score);
}
@Override
public boolean equals(Object o) {
if(this == o)return true;
if(o==null||getClass()!=o.getClass())return false;
Student stu = (Student) o;
return age == stu.age && score == stu.score && Objects.equals(name,stu.name);
}
//比较规则自定义
@Override
public int compareTo(Student o) {
return score-o.score;
}
}
测试类中定义一个数据定义的方法
//创建数据集合并返回数据流对象
public static Stream<Student> getDataSource(){
//实例化集合存储Student对象
ArrayList<Student> students = new ArrayList<>();
Collections.addAll(students,new Student("01",19,90),
new Student("02",20,88),
new Student("03",21,89),
new Student("04",22,97),
new Student("05",23,91),
new Student("06",28,86),
new Student("05",23,91),
new Student("06",18,86)
);
Stream<Student> stream = students.stream();
return stream;
}
过滤函数filter
过滤函数,其参数是一个断定性的接口
//中间操作,过滤,使用函数过滤不及格的学生
public static void filterDemo(){
Stream<Student> dataSource = getDataSource();
dataSource.filter(s->s.getScore()>89).forEach(System.out::println);
}
去重函数distinct
去掉流中重复的元素,因此自定义的类型需要重写hashCode和equals方法。
//distinct 可以去掉指定流中的重复数据
public static void distinct(){
Stream<Student> dataSource = getDataSource();
Stream<Student> distinct = dataSource.distinct();
distinct.forEach(System.out::println);
}
排序函数sorted
sorted有带参数的和不带参数的两种
sorted():默认排序,指定的类型重写了 Comparable
重写比较的方法 sorted(Comparator comp):采用lambda表达式传递参数
/*
* sorted():默认排序可以实现比较接口,自定义排序规则
* sorted(Comparator comp):采用lambda表达式传递参数
* */
public static void sortedUse(){
Stream<Student> dataSource = getDataSource();
//对流数据进行排序
// dataSource.sorted().forEach(System.out::println);
//对流中的数据按照自定义的规则进行
dataSource.sorted((e1,e2)->{return e1.getAge()-e2.getAge();}).forEach(System.out::println);
}
limit & skip
limit限制,表示截取流中指定数量的数据
skip跳过,表示跳过指定数据,截取剩余部分
//limit&& skip
public static void limitandskip(){
Stream<Student> dataSource = getDataSource();
dataSource.distinct()
.sorted((s1,s2)->{return s2.getScore()-s1.getScore();})
.limit(5) //分数最高的五个人
.skip(2) //跳过2的剩余的输出
.forEach(System.out::println);
}
map & mapToInt
//中间操作:map
//提供一个映射规则,将流中的每一个元素替换成指定的元素
public static void mapUse(){
Stream<Student> dataSource = getDataSource();
//获取所有学生的姓名
// dataSource.map(s->s.getName()).forEach(System.out::println);
//获取所有学生的成绩
// dataSource.map(Student::getScore).forEach(System.out::println);
IntSummaryStatistics intSummaryStatistics = dataSource.mapToInt(Student::getScore).summaryStatistics();
System.out.println(intSummaryStatistics.getMax());
}
flatMap
//中间操作:flatmap
//扁平化映射:一般在map映射完成后,流中大的数据是一个容器,而我们需要对容器中的数据进行处理
//使用扁平化映射,可以将流中的容器中的数据直接读取到流中
public static void flatMap(){
String[] array = {"hello","world"};
//将字符串数组中的数据读取到流中
Stream<String> stream = Arrays.stream(array);
// stream.map(String::toCharArray).forEach(e-> System.out.println(e));
stream.map(s->s.split(""))
.flatMap(Arrays::stream)
.distinct()
.forEach(System.out::println);
}
Collections工具类
为了方便获取一个Collector接口的实现类对象,从而使用collect方法,对流中的数据进行处理
方法 | 描述 |
---|---|
Collections.toList() | 将流中的数据,整合到一个List集合中 |
Collections.toSet() | 将流中的数据,整合到一个Set集合中 |
Collections.toMap() | 将流中的数据,整合到一个Map集合中 |
maxBy() | 按照指定的规则,找到流中最大的元素,等同于max |
minBy() | 按照指定的规则,找到流中最小的元素,等同于min |
joining() | 将流中的数据拼接成一个字符串,(智能操作String的字符串) |
summingInt() | 将流中的数据,映射成int类型的数据,并求和 |
averageingInt() | 将流中的数据,映射成int类型的数据,并求平均值 |
summarizingInt() | 将流中的数据,映射成int类型的数据,并获取描述信息 |
public static void main(String[] args) {
//准备字符串数组,作为数据源
String[] dataSource = {"xing","love","the","book"};
//读取数据源中的数据,做成Stream
Stream<String> stream = Arrays.stream(dataSource);
//1.joining只适用于Stream<String>流中
// String collect = stream.collect(Collectors.joining(" ","{","}"));
// System.out.println(collect);
//2.流中的数据映射成int类型并求和
// Integer collect = stream.collect(Collectors.summingInt(String::length));
// System.out.println(collect);
//可以替换上面的两行
System.out.println(stream.mapToInt(String::length).sum());
}