JAVA流式计算
流的简单介绍
Java 8 中,引入了流(Stream)的概念,利用提供的Stream API,我们可以方便的操作集合数据,这种方式很类似于使用SQL对数据库的操作。
如何生成流
利用Stream API,首先我们需要生成流,以下是生成流的常用方式(这里我们只介绍顺序流):
1、所有继承自Collection的接口都可以直接转化为流:
List<Integer> l1 = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = l1.stream();
Map<String,Student> s1 = new HashMap<>();
Stream<Map.Entry<String, Student>> stream1 = s1.entrySet().stream();
2、利用Arrays类中的stream()方法:
Integer[] i1 = new Integer[]{1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(i1);
3、使用Stream类中的静态方法:
Stream<Integer> stream = Stream.of(1,2,3,4,5);
4、利用BufferedReader读取文本中的内容转化为流:
BufferedReader reader = new BufferedReader(new FileReader("D:\\stream.txt"));
Stream<String> stream = reader.lines();
我们经常使用的还是方式1,将集合转化为流。
如何操作流
流的主要操作有筛选/切片/查找/匹配/映射/归约
操作流的方法简介
操作流的方法分为两类,一类是惰性求值,一类是及早求值;
惰性求值并不是立刻执行的,而是将求值的过程记录下,在进行及早求值的时候,才会按顺序执行前面的惰性求值,一般的执行过程如下:
Stream.惰性求值.惰性求值. ... .惰性求值.及早求值
区分是惰性求值还是及早求值,可以通过方法的返回值来判断,返回值时Stream类型的就是惰性求值
在介绍如何通过Stream API对流进行操作前,我们首先了解下函数式接口的相关知识(后面介绍的Stream API中会使用到这些函数式接口),这里只作简单介绍。
函数式接口
所谓函数式接口,是指只含有一个抽象方法的接口,一般加@FunctionalInterface注解加以标注,函数式接口可以被隐式地转化为Lamada表达式;
这里我们主要介绍了java.util.function包下的四个常用的函数式接口:
接口 | 方法 | 简介 |
---|---|---|
Consumer | void accept(T t) | 消费接口,接收一个T类型的对象 |
Supplier | T get() | 供给接口,返回一个T类型的对象 |
Function | R apply(T t) | 映射接口,接收一个T类类型的对象转换为R类型的对象 |
Predicate | boolean test(T t) | 判断接口,判断一个T类型的对象是否满足某个条件,返回一个boolean类型 |
使用Stream API操作流
首先我们将java.util.stream.stream类中的API进行分类:
方法类型 | 方法名 | |
---|---|---|
惰性求值 | 无状态 | filter();map();mapToInt();mapToLong();mapToDouble;flatMap()... |
有状态 | distinct();sorted();skip();limit()... | |
及早求值 | 非短路操作 | reduce();forEach();collect();min();max();count()... |
短路操作 | anyMatch();allMatch()... |
无状态:元素的处理是独立的,不受前面元素的影响;
有状态:元素的处理不是独立的,受到前面元素的影响;
非短路:必须处理完所有元素才能得到结果;
短路:不需要处理完所有元素就能得到结果;
下面我们介绍下常用的Stream方法
1、filter()
方法定义:
Stream<T> filter(Predicate<? super T> predicate)
方法介绍:
用于对数据进行过滤,筛选出符合条件的数据;
接收一个Predicate的函数接口,用于进行条件的过滤;返回符合条件的数据组成的一个新的流;
代码示例:
List<Integer> l1 = Arrays.asList(1,2,3,4,5);
//过滤出大于2的元素
Stream<Integer> integerStream = l1.stream().filter(s -> s > 2);
2、map()
方法定义:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
方法介绍:
对数据进行处理,将T类型的对象转化为R类型的对象,简单来说就是对流中的数据进行同一项操作;
接收一个Function的函数接口,用于对数据处理;返回R类型的数据组成的一个新的流;
代码示例:
List<String> l2 = Arrays.asList("A", "B", "C");
//将每个元素全部转化为小写
Stream<String> stringStream = l2.stream().map(s -> s.toLowerCase());
注:mapToInt(),mapToLong(),mapToDouble(),这是java针对基本数据类型封装出对应的流,这里就不在介绍;
3、flatMap()
方法定义:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
方法介绍:
将流中的每一个元素转化为一个流,然后将转化的一个个流组合成一个新的流;
接收一个Function的函数接口,用于对数据处理;返回R类型的数据组成的一个新的流;
代码示例:
List<List<Integer>> l3 = Arrays.asList(Arrays.asList(1,2,5,4,7),Arrays.asList(4,8,6,9,4));
//将l3中的每个元素(List),转化为一个个流,然后组合成一个新的流
Stream<Integer> integerStream1 = l3.stream().flatMap(s -> s.stream());
4、sorted()
方法定义:
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
方法介绍:
将流中的数据进行排序,然后排序后的数据组合成一个新的流;
无参的方法,默认按照升序升序进行排列;
有参的方法,需要传入Comparator接口的一个实现类,按照该实现进行排序操作;
代码示例:
List<Integer> l1 = Arrays.asList(2,1,4,5);
//升序排列
Stream<Integer> sorted = l1.stream().sorted();
//降序排列
Stream<Integer> sorted1 = l1.stream().sorted((t1, t2) -> { return t1 < t2 ? 1 : t1 == t2 ? 0 : -1; });
5、reduce()
方法定义:
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);
方法介绍:
将流中的数据进行规约,返回规约后的数据;
Optional<T> reduce(BinaryOperator<T> accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。
T reduce(T identity, BinaryOperator<T> accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner):在串行流(stream)中,第三个参数combiner不会起作用,该方法跟第二个方法一样。在并行流(parallelStream)中,道流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是对每个线程的执行结果进行规约,最终返回结果。
代码示例:
List<Integer> l1 = Arrays.asList(2,1,4,5);
//求和,返回的是Optional对象
Optional<Integer> reduce = l1.stream().reduce((t1, t2) -> t1 + t2);
//求和返回的是Integer对象
Integer reduce1 = l1.stream().reduce(0, (t1, t2) -> t1 + t2);
//每个单独的并行流进行求和操作,并行流输出的结果进行相乘操作输出最终结果
Integer reduce2 = l1.parallelStream().reduce(0, (t1, t2) -> t1 + t2, (s1, s2) -> s1 * s2);
6、collect()
方法定义:
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);
方法介绍:
将流中的数据转化为一个新的数据结构;
<R, A> R collect(Collector<? super T, A, R> collector):传入一个想要输出的集合类型,一般通过Collectors创建集合类型
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner):第一个参数supplier为结果存放容器,第二个参数accumulator为结果如何添加到容器的操作,第三个参数combiner则为多个容器的聚合策略
代码示例:
//将流转化为List数据结构
List<Integer> collect1 = Stream.of(1, 2, 3).collect(Collectors.toList());
//将流转化为Map结构
Student student1 = new Student("xiaoni", "male", 15);
Student student2 = new Student("xiaohua", "female", 20);
List<Student> l1 = new ArrayList<>();
l1.add(student1);
l1.add(student2);
HashMap<String, Student> collect = l1.stream()
.collect(() -> new HashMap<String, Student>(),
(h, v) -> {h.put(v.getName(),v);},
HashMap::putAll);
7、综合训练
public static void main(String[] args) throws FileNotFoundException {
Student s1 = new Student("xiaoni", "male", 18);
Student s2 = new Student("xiaohua", "female", 20);
Student s3 = new Student("xiaodong", "male", 19);
Student s4 = new Student("xiaoben", "male", 24);
Student s5 = new Student("xiaoyun", "female", 23);
Student s6 = new Student("xiaojing", "female", 20);
List<Student> l1 = new ArrayList<>();
l1.add(s1);
l1.add(s2);
l1.add(s3);
l1.add(s4);
l1.add(s5);
//统计男生的数量
long count = l1.stream().filter(s -> "male".equals(s.getSex())).count();
System.out.println("男生数量:"+count);
//按年龄从大到小进行排序
List<Student> collect = l1.stream()
.sorted((k1,k2) ->
{
int a1 = k1.getAge();
int a2 = k2.getAge();
return a1 < a2 ? 1 : a1 == a2 ? 0 : -1;
})
.collect(Collectors.toList());
System.out.println("按年龄排序:" + collect);
//求所有学生的年龄之和
long count1 = l1.stream().mapToInt(s -> s.getAge()).sum();
System.out.println("年龄之和:" + count1);
//将数据存放到map中,学生姓名作为key值
Map<String, Student> collect1 = l1.stream().collect(Collectors.toMap(Student::getName, s -> s));
System.out.println("map中的学生:"+collect1);
List<Integer> l2 = Arrays.asList(1,5,4,7,9,2,4,5,1);
//输出最小的三个数字,不包含重复的数字
List<Integer> l3 = l2.stream().distinct().sorted(Integer::compareTo).limit(3).collect(Collectors.toList());
System.out.println("最小的三个数:"+l3);
//输出第二小的数字
List<Integer> l4 = l2.stream().distinct().sorted(Integer::compareTo).skip(1).limit(1).collect(Collectors.toList());
System.out.println("第二小的数字:"+l4);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。