JDK8.0相关特性
六、 Stream(流)
-
流(Stream):是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。Stream提供了强大的数据集合操作功能,但是它不能保存数据,像是一个高级的Iterator。
- 注意:
- 集合讲的是数据(集合用于存储数据),流讲的是计算(流用于对集合中数据的操作)
- Stream本身不能应用存储元素
- Stream不会改变源对象
- 注意:
-
Stream(流)的操作三个步骤:创建Stream、中间操作、终端操作
-
创建Stream(从数据源获取):一个数据源(如集合、数组),获取一个流(Stream)
- 获取Stream流的几种方式
//1. 可以通过Collection 系列集合提供的stream()或是 parallelStream() List<String> list= new ArrayList<String>(); Stream<String> stream1=list.stream(); //2. 通过Arrays中的静态方法stream() 获取数组流 Student[] s = new Student[10]; Stream stream2=Arrays.stream(s); //3. 通过Stream类中的静态方法of() Stream<String> stream3=Stream.of("aa","bb","cc"); //4. 创建无限流 //迭代 Stream<Integer> stream4=Stream.iterate(0,(x)-> x+2); stream4.forEach(System.out::println);//终端操作 //自动生成 Stream stream5=Stream.generate(()->Math.random()); stream5.forEach(System.out::println);
-
中间操作:一个中间操作链,对数据源的数据进行处理
1. 筛选与切片中间操作: filter(Predicate<T> f) : 接受Lambda表达式,从流中筛选某些元素 limit(long n): 截断流,使其元素不超过给定数量 skip(long n):跳过流中的n个元素 distinct():去除流中的重复元素
- 案例:
//获取年龄大于36的学生信息 Stream stream1=stus.stream().filter((s)->s.getAge()>36); //stream1.forEach(System.out::println); //获取2名年龄大于36的学生信息 Stream stream2=stus.stream(). filter((s)->s.getAge()>36).limit(2); stream2.forEach(System.out::println); //跳过2名年龄大于36的之后的学生信息 Stream stream3=stus.stream(). filter((s)->s.getAge()>36).skip(2); stream3.forEach(System.out::println); //龄大于36的学生信息,要求去除内容重复的元素 Stream stream4=stus.stream(). filter((s)->s.getAge()>36).distinct(); stream4.forEach(System.out::println);
2.映射 map<Function<T,R> f>:接收一个函数(Function形式的Lambda表达式)作为 参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素,返回值类 型为Stream<R>。 flatMap<Function<T,Stream<R>>>:接收一个函数作为参数,将流中的每一 个值都换成另一个流,然后把所有流连城一个流Stream<R>。
* 案例1:
//将集合中元素小写转换为大写 List<String> list= Arrays.asList("aa","bb","cc"); Stream<String> stream6=list.stream(). map((str)-> str.toUpperCase()); stream6.forEach(System.out::println); //获取集合(泛型为学生对象)中学生的姓名 stus.stream().map((s)->s.getName()). forEach(System.out::println); ------------------------------------------------------- //将上面List集合中所有的元素的组成字符进行打印输出 public class TestFlagMap{ public static void main(String[] args){ List<String> list= Arrays.asList("aa","bb","cc"); Stream<Stream<Character>> stream7=list.stream(). map(TestStream::filterCharacter); stream7.forEach( (stream)->stream.forEach(System.out::println) ); list.stream().flatMap(TestStream::filterCharacter). forEach(System.out::println); } public static Stream<Character> filterCharacter(String str) { List<Character> list= new ArrayList<>(); for (Character c:str.toCharArray()){ list.add(c); } return list.stream(); } }
3. 排序 sorted(): 自然排序 sorted(Comparator com):定制排序
- 案例
List<String> list= Arrays.asList("ff","aa","ss","bb","cc","dd"); list.stream().sorted().forEach(System.out::println); -------------------------------------------------------------- List<Student> stus= Arrays.asList(new Student("张三",38), new Student("李四",40), new Student("王五",37), new Student("胡杨",35) ); stus.stream().sorted((s1,s2)->{ if(s1.getAge()==s2.getAge()){ return s1.getName().compareTo(s2.getName()); } else{ return s1.getAge()-s2.getAge(); } }).forEach(System.out::println);
* 注意
多个中间操作可以连接形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,并且在终止操作时一次性全部处理,称为"惰性求值"
-
终端操作(终止操作):一个终止操作,执行中间操作链,并产生结果
- 常见的终端操作:
1.基础终端操作 allMatch(Predicate<T>):检查是否匹配所有的元素,返回值类型为bollean anyMatch(Predicate<T>):检查是否至少匹配一个元素,返回值类型为bollean noneMatch(Predicate<T>):检查是否没有匹配任何元素,返回值类型为bollean findFirst():返回第一个元素,类型为Optional<T>,利用get方法获取员孙 findAny():返回当前流中的任意元素 count():返回流中元素的个数,返回值类型为long max(Comparator<T> com):返回流中最大值,返回类为Optional min(Comparator<T> com):返回流中最小值 forEach(Consumer c):内部迭代
- 案例:
List<Student> list = Arrays.asList( new Student("zhangsan",38, Student.Status.Study), new Student("lisi",27, Student.Status.Study), new Student("tom",30, Student.Status.Sleep), new Student("hanmeimei",25, Student.Status.Sleep), new Student("lihua",32, Student.Status.Sleep) ); //判断年龄大于26的学生是否都在学习 boolean r=list.stream(). filter((s)->s.getAge()>26). allMatch( (s)->s.getStatus().equals(Student.Status.Study) ); System.out.println(r); //按照年龄排序,获取排序后的第一个学生信息 Optional<Student> o=list.stream(). sorted((s1, s2)->s1.getAge()-s2.getAge()).findFirst(); System.out.println(o.get()); //随机获取一个睡觉学生的信息 Optional<Student> o2=list.stream().filter( (s)->Student.Status.Sleep.equals(s.getStatus()) ).findAny(); System.out.println(o2.get()); //获取睡觉学生的个数 long count=list.stream().filter( (s)>Student.Status.Sleep.equals(s.getStatus()) ).count(); System.out.println("睡觉学生个数:"+count); //获取学生中年龄最大的信息 Optional<Student> o3=list.stream().max( (s1,s2)->s1.getAge()-s2.getAge()); System.out.println(o3.get()); //获取学生中年龄最小的信息 Optional<Student> o4=list.stream().min( (s1,s2)->s1.getAge()-s2.getAge()); System.out.println(o4.get());
2.规约操作 reduce(T identity, BinaryOperator<T> acc):将流中的数据反复结合起来,得到一个值,返回值为T类型 reduce(BinaryOperator<T> acc):将流中元素反复结合起来,得到一个值,返回值类型为Optional<T>。 注意:map和reduce的连接通常称为map-reduce模式。
- 案例:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8); //获取集合中所有元素的加和 Integer n = list.stream().reduce(0,(x,y)->x+y); System.out.println(n); ----------------------------------------------------------- List<Student> list2 = Arrays.asList( new Student("zhangsan",38, Student.Status.Study), new Student("lisi",27, Student.Status.Study), new Student("tom",30, Student.Status.Sleep), new Student("hanmeimei",25, Student.Status.Sleep), new Student("lihua",32, Student.Status.Sleep) ); Integer sum=list2.stream().map( (s)->s.getAge()).reduce(0,(a1,a2)->a1+a2); System.out.println(sum);
3.收集 collect(Collector<T, A, R> collector):将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。(位于Stream中的方法) 收集器常用接口和方法: Collector<T,A,R>:T - 元素类型;A - 中间类型;R-结果类型 Collectors:获取收集器的工具类,其中定义大量静态方法 (1) toList():将流转换为List类型 (2) toSet():将流转换为Set类型 (3) counting():统计元素个数,返回值类型为long (4) summarizingInt(ToIntFunction<T> f):获取int类型的总和 (5) averagingInt(ToIntFunction<T> f):获取int类型的平均值 (6) maxBy(Comparator<T> com):获取最大值,返回值类型Optional<T> (7) minBy(Comparator<T> com):获取最小值 (8) joining()/joining(String s):字符串的拼接,返回值类型为String (9) 分组操作: ① groupingBy(Function(K,T) f):将流中元素按照指定内容进行分组,转 换结果为Map<T,List<K>> (注意:Map中的键是指定分组分依据内容,K是 分组元素) ② groupingBy(Function<T,K> f,Collector<T,?,D> c):多级分组 (10)分区操作: partitioningBy(Predicate<T> p):按照内容将流中元素划分为false和 true两个区,转换结果类型为Map<Boolean,List<T>>
- 案例:
//将学生姓名存储在List集合中 List<String> l=list2.stream(). map((s)->s.getName()).collect(Collectors.toList()); l.forEach(System.out::println); //将学生姓名存储在HashSet集合中 HashSet<String> l=list2.stream().map((s)->s.getName()). collect(Collectors.toCollection(HashSet::new)); l.forEach(System.out::println); //获取集合中所有学生的年龄总和 int sum2=list2.stream().collect( Collectors.summarizingInt((s)->s.getAge())); //获取年龄最大的学生信息 Optional<Student> s=list2.stream().collect( Collectors.maxBy((s1, s2)->s1.getAge()-s2.getAge())); System.out.println(s); //将集合中所有学生的姓名进行字符串拼接 String s2=list2.stream().map((s1)->s1.getName()). collect(Collectors.joining("-")); --------------------------分组---------------------------------- //将集合中元素按照状态进行分组 Map<Student.Status,List<Student>> map=list2.stream().collect( Collectors.groupingBy((s3)->s3.getStatus())); //将集合中元素先按照状态分组,再按照学生年龄分组:年龄<30,返回青年,否则中年 Map<Student.Status,Map<String,List<Student>>> map2=list2.stream(). collect(Collectors.groupingBy( (s3)->s3.getStatus(),Collectors.groupingBy((s3)->{ if(s3.getAge()<30){ return "青年"; }else { return "中年"; } }))); //获取每种状态下学生的平均年龄 Map<Student.Status,Double> map3=list2.stream().collect( Collectors.groupingBy((s3)->s3.getStatus(), Collectors.averagingDouble((s3)->s3.getAge()))); //将集合中按照年龄是否大于30分成两个小组 Map<Boolean,List<Student>> map4=list2.stream(). collect(Collectors.partitioningBy((s3)->s3.getAge()>30)); -------------------------------------------------------------- DoubleSummaryStatistics sum2=list2.stream().collect( Collectors.summarizingDouble((s3)->s3.getAge())); System.out.println(sum2.getMax()); System.out.println(sum2.getAverage()); System.out.println(sum2.getCount()); System.out.println(sum2.getMin());
以上案例中的Student类: class Student{ private String name; private Integer age; private Status Status; public Student() {} public Student(String name, Integer age) { this.name = name; this.age = age; } public Student(String name,Integer age,Student.Status status) { this.name = name; this.age = age; Status = status; } public Student.Status getStatus() { return Status; } public void setStatus(Student.Status status) { Status = status; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } public enum Status{ Study,Play,Sleep; } }
-