• java8知识点总结


    一、java8的四类函数式接口

    Consumer<T> 消费型

    void accept(T t); 接收一个输入参数,无返回值。

    Supplier<T> 供给型

    T get(); 无参数,返回一个结果。

    Function<T,R> 文档型

    R apply(T t); 接收一个输入参数,返回一个结果。

    Predicate<T> 断言型

    boolean test(T t); 接收一个输入参数,返回一个布尔值结果。

    二、Stream创建流的几种方式

    空Stream(Empty Stream)

    方法empty()被用于创建一个Empty Stream:

    Stream<String> streamEmpty = Stream.empty();
    

    上述代码段创建的Empty Stream通常被用于避免null对象或零元素对象的streams(streams
    with no element)返回结果为null。

    public Stream<String> streamOf(List<String> list){
      return list == null || list.isEmpty() ? Stream.empty() : list.streams();
    }
    

    集合Steram(Stream of Collection)

    可以创建任意Collection接口衍生类(Collection->List、Set、Queue)的Streams:

    Collections<String> collection = Arrays.asList("a", "b", "c");
    Stream<Stirng> streamOfCollection = collection.stream();
    

    数组Stream(Stream of Array)

    创建数组Stream:

    Stream<String> streamOfArray = Stream.of("a", "b", "c");
    

    可以选择Stream中包含的元素数量:

    String[] arr = new String[]{"a", "b", "c"};
    Stream<String> streamOfArrayFull = Arrays.stream(arr);
    Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 3);
    备注:截取的是(1,3]  ---->  输出的是b和c
    

    构建器(Stream.builder())

    当builder被用于指定参数类型时,应被额外标识在声明右侧,否则方法build()将创建一个Stream(Object)实例:

    Stream<String> streamBuilder = Stream.<String>builder()
            .add("a").add("b").add("c")
            .build();
    

    生成器(Stream.generator())

    方法generator()接受一个供应器Supplier<T>用于元素生成。由于生产流(resulting
    stream)被定义之后属于无限流(即无止境地不断生产),开发者必须指定stream拥有流的目标大小,否则方法generator()将持续生产直到jvm内存到达顶值(memory limit):

    Stream<String> streamOfGenerated = Stream.generate( () -> "element").limit(10);
    

    上述代码将创建十个内容为“element”的生成流。

    迭代器(Stream.iterate())

    另一种创建无限流的方法是通过调用方法iterate(),同样的它也需要使用方法limit()对目标流的元素大小进行限制:

    Stream<Integer> streamItreated = Stream.iterate(40, n -> n + 2).limit(20);
    

    迭代流即采用迭代的方法作为元素生产方式,类似于高中数学中的f(x),f(f(x)),etc。上述例子中,生成流的第一个元素是迭代器iterate()中的第一个元素40,从第二个元素开始的每个新元素都与上个元素有关,在此例中,生成流中的元素为:40、42、44 ... 76、78。

    基元流(Stream of Primitives)

    Java8提供了创建三大基础数据类型(int、long、double)stream的方式。由于Stream<T>是一个类接口,我们无法采用泛型传参的方式声明基础数据类型的stream,因此三个特殊的接口就被创造出来了:IntStream、LongStream、DoubleStream。使用它们能够避免不必要的自动装箱,以提高生产效率。

    ①IntStream

    IntStream intStream = IntStream.range(1, 3);
    

    方法range(int startInclusive, int
    endInclusive)创建了一个有序流(从startInclusive到endInclusive)。它使后面的值每个增加1,但却不包括最后一个参数,即[),最终结果是1,2。

    ②LongStream

    LongStream longStream = LongStream.rangeClosed(5, 300);
    

    方法rangeClosed(int startInclusive, int endInclusive)与range()大致相同,它包括最后一个参数,即(),最终结果是5,6,7.....299,300

    ③DoubleStream

    Random random = new Random();
    DoubleStream doubleStream = random.doubles(3);
    

    Java8之后,类Random也提供了拓展方法用于生成基础数据类型的stream,上述代码创建了一个含有3个随机数的DoubleStream。

    字符串流(Stream of String)

    String类型也可以作为生成stream的源,这得益于方法chars()的帮助,此外由于JDK中没有CharStream接口,IntStream也被用来表示字符流(stream
    of chars)

    IntStream streamOfChars = "2a".chars();
    

    下例中通过正则表达式将a,b,c截取为字符串流。

    Stream<String> streamOfString = Pattern.compile(",").spitAsStream("a,b,c");
    

    文件流(Stream of File)

    Java NIO类文件允许通过方法lines()生成文本文件的Stream<String>,文本的每一行都会变成stream的一个元素:

    Path path = Paths.get("C:\file.txt");
    Stream<String> streamOfString = Files.lines(path);
    Stream<String> streamWithCharset = Files.lines(path, Charset.forName("utf-8"));
    

    在方法lines()中也可以通过Charset设置文件编码。

    引用Stream(Referencing a Stream)

    只要调用生成操作(中间操作)就会实例化一个stream并生成一个可获取的引用,但执行终端操作会使得stream无法访问。

    以下代码如果不考虑冗长的话将是有效的:

    Stream<String> stream = Stream.of("a", "b", "c")
                    .filter(t -> t.contains("b"));
    Optional<String> anyElement = stream.findAny();
    

    但是倘若我们在执行终端操作后重新使用相同的引用,则会不可避免的触发IllegalStateException。

    Optional<String> firstElement = stream.findFirst();
    

    IllegalStateException是一个运行时异常(RuntimeException),即编译器将不会提示此错误。因此必须记得JAVA8不允许重复使用stream。因为stream从设计上是用于操作数据源(集合、数组)所生成的元素序列,而不是存储元素。

    所以想让上面的代码正常工作就得改为(List可以存储元素):

    List<String> elements = Stream.of("a", "b", "c")
    .filter(t -> t.contains("b"))
                .collect(Collectors.toList());
    Optional<String> anyElement = elements.stream().findAny();
    Optional<String> firstElement = elements.stream().findFirst();
    

    三、中间操作

    Filter(过滤)

    过滤流中的某些元素

    Stream<T> filter(Predicate<? super T> predicate);
    

    案例

    String[] dd = { "a", "b", "c" };
    Stream<String> stream = Arrays.stream(dd);
    stream.filter(str -> str.equals("a"))
          .forEach(System.out::println);//返回字符串为a的值
    

    Map(映射)

    接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    

    案例

    //利用map把int转string
    Integer[] dd = {1, 2, 3};
    Stream<Integer> stream = Arrays.stream(dd);
    stream.map(str -> Integer.toString(str))
            .forEach(str -> {
                System.out.println(str);//1,2,3
            });
    
    //利用map把对象里的name参数提取成一个List<String>
    List<Emp> list = Arrays.asList(new Emp("a"), new Emp("b"));
    List<String> list1 = list.stream()
    .map(Emp::getName)
    .collect(Collectors.toList());
    

    flatMap

    接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    

    案例

    List<String> list = Arrays.asList("a,b,c", "1,2,3");
    Stream<String> newStream = list.stream().flatMap(s -> {
         String[] split = s.split(",");
         return Arrays.stream(split);
    });
    newStream.forEach(System.out::print);//abc123
    

    distinct,sorted,peek,limit,skip

    //去重
    Stream<T> distinct();
    
    //排序
    Stream<T> sorted();
    
    //定制排序,自定义Comparator排序器
    Stream<T> sorted(Comparator<? super T> comparator);
    
    //如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
    Stream<T> peek(Consumer<? super T> action);
    
    //获取maxSize个元素
    Stream<T> limit(long maxSize);
    
    //跳过n个元素
    Stream<T> skip(long n);
    

    案例

    List<Emp> empList = new ArrayList<>();
    empList.add(new Emp("张三", 45, 9000.0));
    empList.add(new Emp("李四", 55, 10000.0));
    empList.add(new Emp("王五", 20, 1500.0));
    empList.add(new Emp("赵六", 25, 2500.0));
    empList.add(new Emp("田七", 30, 4000.0));
    
    //获取3个年龄>25岁的员工信息,根据工资排序
    List<Emp> emps1 = empList.stream().filter(t -> t.getAge() > 25)
            .sorted(Comparator.comparing(Emp::getSalary))
            .distinct()
            .limit(3)
            .collect(Collectors.toList());
    emps1.forEach(System.out::println);
    
    
    //把年龄>30岁的员工工资 x 1.5倍
    List<Emp> emps2 = empList.stream().filter(t -> t.getAge() > 30)
            .peek(emp -> {
                emp.setSalary(emp.getSalary() * 1.5);
            })
            .collect(Collectors.toList());
    emps2.forEach(System.out::println);
    
    
    //数字从1开始迭代,跳过前5个数再往后获取10个数
    List<Integer> nums = Stream.iterate(1, i -> i + 1)
            .skip(5)
            .limit(10)
            .collect(Collectors.toList());
    nums.forEach(System.out::println);//输出6,7,8.....14,15
    

    四、终端操作

    forEachOrdered和forEach

    forEachOrdered按指定的顺序处理流元素,而不管流是顺序的还是并行的。

    forEach在并行流中不仅以非确定顺序执行,还可以在不同线程中针对不同元素同时执行。

    void forEach(Consumer<? super T> action);  
    void forEachOrdered(Consumer<? super T> action);
    

    案例

    List<String> list = Arrays.asList("a", "b", "c");
    //顺序流时都是按顺序操作
    list.stream().forEachOrdered(System.out::print);//abc
    list.stream().forEach(System.out::print);//abc
    
    //并行流时forEachOrdered能按顺序操作,而forEach不能
    list.parallelStream().forEachOrdered(System.out::print);//abc
    list.parallelStream().forEach(System.out::print);//不确定
    

    toArray

    public final <A> A[] toArray(IntFunction<A[]> generator)
    

    案例

    List<String> strs = Arrays.asList("a", "b", "c");
    String[] str1 = strs.stream().toArray(str -> new String[strs.size()]);
    String[] str2 = strs.stream().toArray(String[]::new);
    String[] str3 = strs.toArray(new String[strs.size()]);
    
    Object[] obj1 = strs.stream().toArray();
    Object[] obj2 = strs.toArray();
    

    count,max,min

    //返回流中元素的总个数
    long count();
    
    //返回流中元素最大值  
    Optional<T> max(Comparator<? super T> comparator);
    
    //返回流中元素最小值
    Optional<T> min(Comparator<? super T> comparator);
    

    findFirst,findAny

    //返回流中第一个元素
    Optional<T> findFirst();
    
    //返回流中的任意元素
    Optional<T> findAny();
    

    案例

    List<String> list = Arrays.asList("a", "b", "c", "d");
    
    String findFirst = list.stream().findFirst().get();
    String findAny = list.stream().findAny().get();
    

    anyMatch,allMatch,noneMatch

    //只要流中有一个元素满足该断言则返回true
    boolean anyMatch(Predicate<? super T> predicate);  
    
    //当流中每个元素都符合该断言时才返回true
    boolean allMatch(Predicate<? super T> predicate); 
    
    //当流中每个元素都不符合该断言时才返回true
    boolean noneMatch(Predicate<? super T> predicate);
    

    案例

    List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
    boolean aa = strs.stream()
            .anyMatch(str -> str.equals("a"));//true
    
    boolean bb = strs.stream()
            .allMatch(str -> str.equals("a"));//false
    
    boolean cc = strs.stream()
            .noneMatch(str -> str.equals("a"));//false
    

    reduce

    reduce
    是一种归约操作,将流归约成一个值的操作叫做归约操作,用函数式编程语言的术语来说,这种称为折叠(fold)

    ①第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。

    Optional<T> reduce(BinaryOperator<T> accumulator);
    

    案例

    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Integer sum = list.stream().reduce((x1, x2) -> x1 + x2).get();
       等同于      list.stream().reduce(Integer::sum).get();
    System.out.println(sum); // 55
    

    ②流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。

    T reduce(T identity, BinaryOperator<T> accumulator);
    

    案例

    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Integer sum = list.stream().reduce(10, (x1, x2) -> x1 + x2);
    System.out.println(sum); //65
    

    ③在顺序流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。

    在并行流(parallelStream)中,我们知道流被fork
    join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。

    <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
    

    顺序流案例:

    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    
    Integer i1 = list.stream().reduce(0,
            (a1, a2) -> {
                System.out.println("stream accumulator: a1:" + a1 + " a2:" + a2);
                return a1 - a2;
            },
            (a1, a2) -> {
                System.out.println("stream combiner: b1:" + a1 + " b2:" + a2);
                return a1 * a2;
            });//顺序流中,粉色部分不会执行
    System.out.println(i1); //-55
    

    并行流案例

    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    
    Integer i2 = list.parallelStream().reduce(0,
            (a1, a2) -> {
                System.out.println("parallelStream accumulator: a1:" + a1 + " a2:" + a2);
                return a1 - a2;
            },
            (a1, a2) -> {
                System.out.println("parallelStream combiner: b1:" + a1 + " b2:" + a2);
                return a1 * a2;
            });//并行流中,粉色部分会执行
    System.out.println(i2); //3628800
    

    collect

    <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);
    

    案例

    List<Emp> list = new ArrayList<>();
    list.add(new Emp("张三", 45, 9000.1));
    list.add(new Emp("李四", 55, 10000.0));
    list.add(new Emp("王五", 20, 1500.0));
    list.add(new Emp("赵六", 25, 2550.0));
    list.add(new Emp("田七", 30, 4100.0));
    
    // 转list
    List<String> names1 = list.stream().map(Emp::getName).collect(Collectors.toList());
    
    // 转set
    Set<String> names2 = list.stream().map(Emp::getName).collect(Collectors.toSet());
    
    // 转map,需要指定key和value,Function.identity()表示当前的Emp对象本身
    Map<String, Emp> map = list.stream().collect(Collectors.toMap(Emp::getName, Function.identity()));
    
    // 计算元素中的个数
    Long count = list.stream().collect(Collectors.counting());
    Long count1 = (long) list.size();
    
    // 数据求和 summingInt summingLong,summingDouble
    Integer sumAges = list.stream().collect(Collectors.summingInt(Emp::getAge));
    
    // 平均值 averagingInt,averagingLong,averagingDouble
    Integer aveAge = list.stream().collect(Collectors.averagingInt(Emp::getAge)).intValue();
    Double aveSalary = list.stream().collect(Collectors.averagingDouble(Emp::getSalary));
    
    // 综合处理类(包含count,sum,min,max,average)
    // summarizingInt,summarizingLong,summarizingDouble
    IntSummaryStatistics intSummary = list.stream().collect(Collectors.summarizingInt(Emp::getAge));
    
    // 连接字符串,可以使用重载的方法,加上一些前缀,后缀和中间分隔符
    String strEmp1 = list.stream().map(Emp::getName).collect(Collectors.joining(","));
    //张三,李四,王五,赵六
    String strEmp2 = list.stream().map(Emp::getName).collect(Collectors.joining("-中间的分隔符-", "最前缀*", "&最后缀"));
    
    // maxBy 按照比较器中的比较结果刷选最大值
    Optional<Integer> maxAge = list.stream().map(Emp::getAge)
            .collect(Collectors.maxBy(Comparator.comparing(Function.identity())));
    
    // 最小值
    Optional<Integer> minAge = list.stream().map(Emp::getAge)
            .collect(Collectors.minBy(Comparator.comparing(Function.identity())));
    System.out.println("max:" + maxAge);
    System.out.println("min:" + minAge);
    
    // 归约操作
    list.stream().map(Emp::getAge).collect(Collectors.reducing((x, y) -> x + y));
    list.stream().map(Emp::getAge).collect(Collectors.reducing(0, (x, y) -> x + y));
    
    // 分操作 groupingBy 根据地址,把原list进行分组
    Map<Integer, List<Emp>> mapGroup = list.stream().collect(Collectors.groupingBy(Emp::getAge));
    
    // partitioningBy 分区操作 需要根据类型指定判断分区
    Map<Boolean, List<Integer>> partitioningMap = list.stream().map(emp -> emp.getAge())
            .collect(Collectors.partitioningBy(emp -> emp > 20));
    

    五、其他

    Optional静态类

    在java8中,很多的stream的终端操作,都返回了一个Optional<T>对象,这个对象,是用来解决空指针的问题,而产生的一个类。

    private Optional() {
        this.value = null;
    }
    
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    
    public static <T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    

    1.get()直接取,如果为null,就返回异常。

    2.orElse(T
    other)在取这个对象的时候,设置一个默认对象(默认值);如果当前对象为null的时候,就返回默认对象。

    3.orElseGet(Supplier<? extends T>
    other)跟第二个是一样的,区别只是参数,传入了一个函数式参数。

    4.orElseThrow(Supplier<? extends X>
    exceptionSupplier)第四个,跟上面表达的是一样的,为null的时候,返回一个特定的异常。

    5. isPresent()是对当前的value进行null判断。

    案例

    Emp emp = new Emp("张三", "上海", "22");
    
    Optional<Emp> op = Optional.ofNullable(emp);
    System.out.println(op.get().getAddress());//上海
    
    Optional<Emp> op1 = Optional.ofNullable(null);
    System.out.println(op1.orElse(emp).getAddress());//上海
    
    //这里指定了一个默认对象emp,为先创建的一个emp对象,emp对象里的成员变量还没有赋值,所以输出为null
    System.out.println(op1.orElseGet(Emp::new).getAddress());
    
    // java.lang.RuntimeException
    try {
        System.out.println(op1.orElseThrow(RuntimeException::new));
    } catch (Exception e) {
        e.printStackTrace();
    }
    
  • 相关阅读:
    如何提高工作效率,重复利用时间
    好记性不如烂笔头
    如何应对面试中关于“测试框架”的问题
    通宵修复BUG的思考
    工作方法的思考
    别认为那是一件简单的事情
    开发人员需要熟悉缺陷管理办法
    不了解系统功能的思考
    如何布置任务
    事事有回音
  • 原文地址:https://www.cnblogs.com/supiaopiao/p/15079223.html
Copyright © 2020-2023  润新知