漫漫人生路-学点Jakarta基础-Java8新特性 Stream/Lambda
背景
Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道
原因
一是集合类持有的所有元素都是存储在内存中的,非常巨大的集合类会占用大量的内存,而Stream的元素却是在访问的时候才被计算出来,这种“延迟计算”的特性有点类似Clojure的lazy-seq,占用内存很少。
二是集合类的迭代逻辑是调用者负责,通常是for
循环,而Stream的迭代是隐含在对Stream的各种操作中,例如map()
。
特点
①不储存。数据流不是存储元素的数据结构;相反,它将元素从源数据结构、数组、生成器函数或输入/输出通道传递到计算操作的管道中。
②功能性。一个流操作产生一个结果,但不修改它的源。例如,从一个集合中过滤得到的数据流产生一个新的流,而不过滤元素,而不是从源集合中移除元素。
③懒惰寻求。许多流操作,如过滤、映射,或去除重复,可以懒洋洋地,暴露的机会,优化。例如,“寻找三个连续的元音字母的第一个字符串”不需要检查所有的输入字符串。流操作分为中间(流生产)操作和终端(价值或副作用生产)操作。中间操作总是懒惰。
④可能无界。虽然集合有一个有限的大小,流不需要。短路操作如极限(n)或findfirst()可以允许无限流计算在有限的时间内完成.
生成(DoubleSteam、IntSteram或LongStream对象三个数值类型)
// 1. 对象 Stream stream = Stream.of("a", "b", "c"); // 2. 数组 String [] strArray = new String[] {"a", "b", "c"}; stream = Stream.of(strArray); stream = Arrays.stream(strArray); // 3. 集合 List<String> list = Arrays.asList(strArray); stream = list.stream();
方法
①filter:是一个中间操作,接受一个predicate接口类型的变量,并将所有流对象中的元素进行过滤。filter(s -> s.getState()==State.pay)
②map:是一个对于流对象的中间操作,通过给定的方法,它能够把流对象中的每一个元素对应到另外一个对象上。
③reduce:把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),返回单个的结果值,并且reduce操作每处理一个元素总是创建一个新值
④limit : 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素
⑤sorted: 一个中间操作,能够返回一个排过序的流对象的视图。流对象中的元素会默认按照自然顺序进行排序,除非你自己指定一个Comparator接口来改变排序规则.
⑥collect: 修改现存的值
⑦Collectors 类的主要作用就是辅助进行各类有用的 reduction 操作
⑧groupingBy 按规则分组:stream().collect(Collectors.groupingBy(p->p.getState()))
⑨partitioningBy 是一种特殊的 groupingBy,它依照条件测试的是否两种结果来构造返回的数据结构,get(true) 和 get(false) 能即为全部的元素对象。
Stream 有三个 match 方法,从语义上说:
①allMatch:Stream 中全部元素符合传入的 predicate,返回 true
②anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
③noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
实例
首先定义一个student类:
package Stream; /** * Created by zjc on 2018/4/9. */ public class Student { public enum Sax{ FEMALE,MALE } private String name; private int age; private Sax sax; private int height; public Student(String name, int age,Sax sax,int height){ this.name=name; this.age =age; this.height = height; this.sax = sax; } public String getName() { return name; } public int getAge() { return age; } public Sax getSax() { return sax; } public int getHeight() { return height; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", sax=" + sax + ", height=" + height + '}'; } }
main方法中创建实例数据:
package Stream; import java.text.DecimalFormat; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.OptionalDouble; import java.util.stream.Collectors; /** * Created by zjc on 2018/4/9. */ public class test { public static void main(String[] args) { List<Student> students = Arrays.asList( new Student("zjc",22,Student.Sax.MALE,180), new Student("白小",28,Student.Sax.FEMALE,170), new Student("mengzhao",31,Student.Sax.MALE,190) ); /**********************************************************************/ //1.输出所有性别为MALE的学生: // 循环: for (Student student : students){ if (student.getSax() == Student.Sax.MALE){ System.out.println(student); } } //使用Stream students.stream() .filter(student -> student.getSax() == Student.Sax.MALE)//過濾 .forEach(System.out::println); /***********************************************************************/ /***********************************************************************/ //2.求出所有学生的平均年龄: /** * average方法得到一个OptionalDouble类型的值, * 这也是Java8的新增特性,OptionalXXX类用于减少空指针异常带来的崩溃, * 可以通过orElse方法获得其值,如果值为null,则取默认值0。 */ OptionalDouble averageAge = students.stream() .mapToInt(Student::getAge)//将对象映射成整形 .average();//根据整型数据求平均值 System.out.println("所有学生的平均年龄为:"+averageAge.orElse(0)); /***********************************************************************/ /***********************************************************************/ //3.输出每个学生姓名的大写形式: List<String> names = students.stream() .map(Student::getName) // 将Student对象映射为String(姓名) .map(String::toUpperCase) // 将姓名转为大写 .collect(Collectors.toList()); // 生成列表 System.out.println("所有学生姓名的大写为:"+names); /***********************************************************************/ /***********************************************************************/ //4按照年龄从小到大排序: List<Student> sortedStudents = students.stream() .sorted((o1, o2) -> o1.getAge() - o2.getAge()) .collect(Collectors.toList()); System.out.println("按照年龄排序后列表为:"+sortedStudents); /***********************************************************************/ /***********************************************************************/ //5.判断是否存在名为zjc 的学生 boolean isContain = students.stream() .anyMatch(student -> student.getName().equals("zjc"));//查询任意匹配项是否存在 System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^"); System.out.println("是否包含姓名为zjc的学生"+isContain); /***********************************************************************/ /***********************************************************************/ //6.将所有学生按照性别分组 Map<Student.Sax,List<Student>> groupBySax = students.stream() .collect(Collectors.groupingBy(Student::getSax)); System.out.println(groupBySax.get(Student.Sax.FEMALE)); /************************************************************************/ /************************************************************************/ //7.求出每个学生身高比例: double sumHeight = students.stream().mapToInt(Student::getHeight).sum(); // 求出身高总和 DecimalFormat formator = new DecimalFormat("##.00"); // 保留两位小数 List<String> percentages = students.stream() .mapToInt(Student::getHeight) // 将Student对象映射为身高整型值 .mapToDouble(value -> value / sumHeight * 100) // 求出比例 .mapToObj(per -> formator.format(per) + "%") // 组装为字符串 .collect(Collectors.toList()); System.out.println("所有学生身高比例:" + percentages); } }