我们一起来学java8的lambda表达式Stream

Posted 竹以南乡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我们一起来学java8的lambda表达式Stream相关的知识,希望对你有一定的参考价值。

Java 8 函数式编程风格

Java 迄今为止最令人激动的特征。这些新的语言特征允许采用函数式风格来进行编码,我们可以用这些特性完成许多有趣的功能。这些特性如此有趣以至于被认为是不合理的.他们说会影响计算速度,但是虽然是真的,但是存在皆合理.

所以我们摒弃缺点,研究优点.

演练

 

/**
 * @Title: Dog.java
 * @Description: Stream的演练
 * @author LiJing
 * @date 2018/12/3 14:13
 * @version 1.0.0
 */

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.text.Collator;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
    private Long id;//编号
    private String name;//名字
    private Integer age;//年龄
    private String team;//分队

    public static void main(String[] args) {
        List<Dog> list = Lists.newArrayList();
        list.add(new Dog(5L, "甜甜", 2, "M"));
        list.add(new Dog(1L, "豆豆", 5, "F"));
        list.add(new Dog(2L, "苗苗", 3, "a"));
        list.add(new Dog(4L, "糖糖", 3, "A"));
        list.add(new Dog(6L, "文文", 3, "G"));
        list.add(new Dog(3L, "晴晴", 4, "g"));

        //提取组别号 转换大写  这里可以看到 一个 Stream 只可以使用一次 map映射有很多方法
        List<String> teamList = list.stream().map(Dog::getTeam).collect(Collectors.toList());
        List<String> listCase = teamList.stream().map(String::toUpperCase).collect(Collectors.toList());

        /**list操作*/
        //1.获取年龄最大/最小的狗
        Dog dog2MaxAge = list.stream().max((d1, d2) -> d1.getAge().compareTo(d2.getAge())).orElse(new Dog());
        Dog dog1MaxAge = list.stream().min(Comparator.comparing(Dog::getAge)).get();

        //2.静态方法 单一操作时候可以简化为如下:
        list.stream().forEach(x -> System.out.println(x));
        list.forEach(System.out::println);

        //3.排序 根据年龄排序
        List<Dog> sortedAgeList = list.stream().sorted(Comparator.comparing(Dog::getAge)).collect(Collectors.toList());
        //4.根据组队号 自然排序
        List<Dog> sortedTeamList = list.stream().sorted(Comparator.comparing(Dog::getTeam)).collect(Collectors.toList());
        //5.根据名字的字母  Collator 类执行区分语言环境的 String 比较
        List<Dog> sortedNameList = list.stream().sorted((o1, o2) -> Collator.getInstance(Locale.CHINESE).compare(o1.getName(), o2.getName())).collect(Collectors.toList());
        //6.去重 去掉重复的结果
        List<Dog> distinctList = list.stream().distinct().collect(Collectors.toList());
        //7.截取 截取流的前N个元素
        List<Dog> limitList = list.stream().limit(3).collect(Collectors.toList());
        //8.跳过流的前n个元素
        List<Dog> skipList = list.stream().skip(4).collect(Collectors.toList());
        //9.将小流合并成一个大流
        List<String> listLine = new ArrayList<String>();
        listLine.add("I am a boy");
        listLine.add("I love the girl");
        listLine.add("But the girl loves another girl");

        //按空格分词 分完词之后,每个元素变成了一个String[]数组
        Stream<String[]> stream = listLine.stream().map(line -> line.split(" "));
        //将每个String[]变成流
        Stream<Stream<String>> streamStream = listLine.stream().map(line -> line.split(" ")).map(Arrays::stream);
        //此时一个大流里面包含了一个个小流,我们需要将这些小流合并成一个流
        Stream<String> stringStream = listLine.stream().map(line -> line.split(" ")).flatMap(Arrays::stream);


        //规约 获取年龄最大的狗狗
        Dog dog = list.stream().reduce((p1, p2) -> {
            System.out.println("p1===" + p1);
            System.out.println("p2===" + p2);
            return p1.getAge() > p2.getAge() ? p1 : p2;
        }).get();
        System.out.println(dog);

        //规约 求和 计算所有狗狗的年龄总和
        //reduce的第一个参数表示初试值为0;
        //求和 那么可以使用Integer提供了sum函数代替自定义的Lambda表达式
        int age2Total = list.stream().mapToInt(Dog::getAge).reduce(0, Integer::sum);

        /**聚合运算*/
        //求和
        long sum = list.stream().mapToLong(Dog::getId).sum();
        //均值
        double average = list.stream().mapToInt(Dog::getAge).average().getAsDouble();


        /**匹配:*/
        //如果流为空,则返回false并且不评估谓词 ==>检查所有的名字是否是以 ‘文‘结尾的  显然不是 [少一个都不行,全检性]  看电影去:<一个都不能少>
        boolean b1 = list.stream().allMatch(x -> x.getName().endsWith("文"));//false
        //如果流为空,则返回false并且不评估谓词 ==>检查是否有在A组的小狗  [存在性]
        boolean b2 = list.stream().anyMatch(x -> "A".compareToIgnoreCase(x.getTeam()) < 0);//true
        //如果流为空,则返回false并且不评估谓词 ==>检查是否有任意小狗 年龄大于3岁  [存在性]
        boolean b3 = list.stream().anyMatch(x -> x.getAge() < 3);//true
        // list = Collections.EMPTY_LIST;
        //如果流为空,则返回true并且不评估谓词 ==> allMatch相反,检查有么狗的年龄大于100岁的   [有一个都不行,全检性]
        boolean b4 = list.stream().noneMatch(x -> x.getAge() > 100);//true

        //findAny list即stream是有顺序的 findAny能够从流中随便选一个元素出来
        Optional<Dog> optionalDog = list.stream().findAny();//isPresent表示是否存在
        System.out.println(optionalDog.isPresent());

        //再看 其实就是 b6就是b7
        boolean b6 = list.stream().filter(x -> x.getAge() < 3).findAny().isPresent();//true
        //过滤
        List<Dog> filterList1 = list.stream().filter(x -> x.getAge() < 3).collect(Collectors.toList());
        boolean b7 = list.stream().anyMatch(x -> x.getAge() < 3);//true 返回在流遍历过程是否遇到过的年龄大于3的狗

        //与guava的getFirst方法类似,Stream.findFirst将返回给定流的第一个元素,如果流为空,则返回空Optional。如果流没有遭遇顺序,则可以返回任何元素
        Optional<Dog> first = list.stream().findFirst();


        /**
         * parallelStream是什么
         * parallelStream其实就是一个并行执行的流.它通过默认的ForkJoinPool,可能提高你的多线程任务的速度.
         * Stream具有平行处理能力,处理的过程会分而治之,也就是将一个大任务切分成多个小任务,这表示每个任务都是一个操作,因此像以下的程式片段
         *
         * 而可能是任意的顺序,就forEach()这个操作來讲,如果平行处理时,希望最后顺序是按照原来Stream的数据顺序,那可以调用forEachOrdered()
         * 如果forEachOrdered()中间有其他如filter()的中介操作,会试着平行化处理,然后最终forEachOrdered()会以原数据顺序处理,
         * 因此,使用forEachOrdered()这类的有序处理,可能会(或完全失去)失去平行化的一些优势,实际上中介操作亦有可能如此,例如sorted()方法
         *
         * parallelStream背后的男人:ForkJoinPool
         * ForkJoinPool主要用来使用分治法(Divide-and-Conquer Algorithm)来解决问题
         * 那么使用ThreadPoolExecutor或者ForkJoinPool,会有什么性能的差异呢?
         * 首先,使用ForkJoinPool能够使用数量有限的线程来完成非常多的具有父子关系的任务,比如使用4个线程来完成超过200万个任务。
         * 但是,使用ThreadPoolExecutor时,是不可能完成的,因为ThreadPoolExecutor中的Thread无法选择优先执行子任务,
         * 需要完成200万个具有父子关系的任务时,也需要200万个线程,显然这是不可行的。
         * */
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        numbers.parallelStream().forEach(System.out::print);
        System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一样");
        numbers.stream().parallel().forEach(System.out::print);
        System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一样");
        numbers.stream().parallel().forEachOrdered(System.out::print);
        System.out.println();

        Optional<Dog> any = list.parallelStream().findAny();
        System.out.println(any.isPresent());

        //并行流 此时的findAny哪一个都有可能返回
        Optional<Dog> any1 = list.stream().parallel().findAny();
        System.out.println(any1);


        /**map操作*/
        //1.转为Map 以编号:key 以名字为value  用::,和x->都可以 推荐::
        Map<Long, String> map1 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x.getName()));

        //2.转为Map 以编号:key 以对象自身为value  用x->x,和Function.identity() 推荐::Function.identity()
        Map<Long, Dog> map2 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x));
        Map<Long, Dog> map3 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity()));

        //3.重复key的情况,这时候流的处理会抛出个异常:Java.lang.IllegalStateException:Duplicate key。
        //这时候就要在toMap方法中指定当key冲突时key的选择。(这里是选择第二个key覆盖第一个key)
        Map<Long, Dog> map4 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity(), (oldKey, newKey) -> newKey));

        //4.根据 年龄or组队 分组用groupingBy 可以分组成多个列表
        Map<Integer, List<Dog>> map5 = list.stream().collect(Collectors.groupingBy(Dog::getAge));
        Map<String, List<Dog>> map6 = list.stream().collect(Collectors.groupingBy(Dog::getName));

        //5.partitioningBy可以理解为特殊的groupingBy,key值为true和false,当然此时方法中的参数为一个判断语句(用于判断的函数式接口)
        Map<Boolean, List<Dog>> map7 = list.stream().collect(Collectors.partitioningBy(o -> o.getAge() < 3));

        //遍历小map
        map6.forEach((k, v) -> System.out.println(k + v));

    }
}

 

 

 

 

 

 Collectors 类的静态工厂方法:

工厂方法返回类型作用
toList List<T> 把流中所有项目收集到一个 List
toSet Set<T> 把流中所有项目收集到一个 Set,删除重复项
toCollection Collection<T> 把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new)
counting Long 计算流中元素的个数
sumInt Integer 对流中项目的一个整数属性求和
averagingInt Double 计算流中项目 Integer 属性的平均值
summarizingInt IntSummaryStatistics 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值
joining String 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", "))
maxBy Optional<T> 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty()
minBy Optional<T> 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty()
reducing 归约操作产生的类型 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
groupingBy Map<K, List<T>> 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键
partitioningBy Map<Boolean,List<T>> 根据对流中每个项目应用谓词的结果来对项目进行分区

 

以上是关于我们一起来学java8的lambda表达式Stream的主要内容,如果未能解决你的问题,请参考以下文章

初识Java8新特性Lambda 之collections

java8 lambda表达式

Java8 Lambda表达式介绍

Java8 Lambda 与匿名类

Java8新特性

Java8新特性