Java 8 中,引入了流(Stream)的概念,利用提供的Stream API,咱们能够方便的操做集合数据,这种方式很相似于使用SQL对数据库的操做。java
利用Stream API,首先咱们须要生成流,如下是生成流的经常使用方式(这里咱们只介绍顺序流):数据库
一、全部继承自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();
二、利用Arrays类中的stream()方法:app
Integer[] i1 = new Integer[]{1,2,3,4,5}; Stream<Integer> stream = Arrays.stream(i1);
三、使用Stream类中的静态方法:ide
Stream<Integer> stream = Stream.of(1,2,3,4,5);
四、利用BufferedReader读取文本中的内容转化为流:函数
BufferedReader reader = new BufferedReader(new FileReader("D:\\stream.txt")); Stream<String> stream = reader.lines();
咱们常常使用的仍是方式1,将集合转化为流。线程
流的主要操做有筛选/切片/查找/匹配/映射/归约code
操做流的方法分为两类,一类是惰性求值,一类是及早求值;对象
惰性求值并非马上执行的,而是将求值的过程记录下,在进行及早求值的时候,才会按顺序执行前面的惰性求值,通常的执行过程以下:排序
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类型 |
首先咱们将java.util.stream.stream类中的API进行分类:
方法类型 | 方法名 | |
---|---|---|
惰性求值 | 无状态 | filter();map();mapToInt();mapToLong();mapToDouble;flatMap()... |
有状态 | distinct();sorted();skip();limit()... | |
及早求值 | 非短路操做 | reduce();forEach();collect();min();max();count()... |
短路操做 | anyMatch();allMatch()... |
无状态:元素的处理是独立的,不受前面元素的影响; 有状态:元素的处理不是独立的,受到前面元素的影响; 非短路:必须处理完全部元素才能获得结果; 短路:不须要处理完全部元素就能获得结果;
下面咱们介绍下经常使用的Stream方法
一、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);
二、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针对基本数据类型封装出对应的流,这里就不在介绍;
三、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());
四、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; });
五、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);
六、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);
七、综合训练
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); }