• Lambda表达式 Stream流


    Lambda表达式

    1.Lambda表达式使用场景:

    Lambda表达式可以取代大部分情况的匿名内部类,注意,正如上面说述,Lambda的应用就是为了取消大部分情况的匿名内部类。(只能替代用于只有一个抽象方法的接口创建匿名内部类的情况,因为需要让程序自己去确定实现的函数以及去创建实例。)[只能是函数式接口,就算是只有一个抽象方法的抽象类也不行!],能够极大的优化代码结构,使代码更加紧凑。

    2.Lambda表达式的基本使用:

    基本语法:(T …) -> { … },其中(T …)是参数列表,{ … }是方法体, -> 是Lambda运算符,读作 goes to。

    Predicate predict = (String val) -> {
      if(val.length()>5){
        return true;
        }else{
        return false;
       }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.Lambda语法的简化:

    (1)简化参数类型,在参数列表中可以省略参数类型(省略必须全部省略)

    Predicate predict = (val) -> {
      if(val.length()>5){
        return true;
        }else{
        return false;
       }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (2)当只有一个参数时,可以省略“( )”

    Predicate predict = val -> {
      if(val.length()>5){
        return true;
        }else{
        return false;
       }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (3)当描述方法体只有一句话时,可以省略“{ }”

    Predicate predict = val ->
      return val.length()>5?true:false;
    
    • 1
    • 2

    (4)当描述方法体只有一句话且是return语句时,可以省略“return”

    Predicate predict = val -> val.length()>5?true:false;
    
    • 1

    4.Lambda表达式的方法引用:

    使用场景:有时候我们不必自己去实现接口中的方法,而是可以直接使用现成的别人写好的方法,这个时候就可以使用Lambda的方法引用。即:使用别人的方法作为函数式接口中未实现的方法,所以对应的要求必须是:参数列表相同,返回值相同,方法名不要求相同。

    (1)基本语法:

    方法归属者::方法名即:静态方法的方法归属者是类名,实例方法的方法归属者是对象。

    Consumer consumer = System.out::println;
    
    • 1

    (2)构造方法引用:

    当函数式接口是返回的某个实例,如果没有特别的功能,就可以直接使用构造方法引用,基本的语法是: 类名::new;

    Supplier supplier = Object::new;
    
    • 1

    5.Lambda表达式的闭包问题:

    匿名内部类在函数中存在的问题,在Lambda中肯定也存在。例如我们在方法中的匿名内部类使用了外部的变量,那么编译器会默认为这个变量加上final修饰,所以下面如果试图去修改这个变量,就会报错:该值是final类型,不能改变。

    Stream流

    1.Stream的基本介绍:

    Java8引入的Stream特性,使得代码结构更加紧凑,简洁易读;且并发模式使得代码执行速度更快。

    2.Stream的构成:Source、Intermediate、Terminal。

    (1)流的构造(Source)

    利用Stream直接构造流:Stream.of(T …)

    Stream stream = Stream.of("as", "dsd", "kojj", "fsdd");
    
    • 1

    利用数组构造流:Arrays.Stream(T[] arr)

    String[] strArr = {"as", "dsd", "kojj", "fsdd"};
    Stream stream = Arrays.stream(strArr);
    
    • 1
    • 2

    集合转换为流:Collection.stream()

    String[] strArr = {"as", "dsd", "kojj", "fsdd"};
    List list = Arrays.asList(strArr);
    Stream stream = list.stream();
    
    • 1
    • 2
    • 3

    文件读取转换为流:java.io.BufferedReader.lines()

    List output = reader.lines().
         flatMap(line -> Stream.of(line.split(REGEXP))).
         filter(word -> word.length() > 0).
         collect(Collectors.toList());
    
    • 1
    • 2
    • 3
    • 4

    自己构建流:Stream.generate(Supplier supplier)
    注意:Supplier实现的流是无限的,要么使用short-circuiting操作,要么使用limit限制流。

    Random random = new Random();
    Stream stream = Stream.generate(random::nextInt);
    stream.limit(10).forEach(System.out::println);
    
    • 1
    • 2
    • 3

    利用Stream.iterator(T seed, Function fun)构建流,无限流需要short-circuiting操作

    Random random = new Random();
    Stream stream3 = Stream.generate(random::nextInt);
    Stream.iterate(0,val->val+3).limit(10).forEach(System.out::println);
    
    • 1
    • 2
    • 3

    注意:对于基本数值类型有对应的IntStream、LongStream以及DoubleStream,也可以使用泛型限定,但是boxing与Unboxing操作会耗时,应优先使用给定的三种数值流。

    (2)转换操作(Intermediate)常用的转换操作主要有:

    map (mapToInt, flatMap 等)、 filter、distinct、sorted、peek、limit、 skip、 parallel、sequential、unordered。

    示例:

    map(Function fun) 一对一处理

    List output = wordList.stream().
        map(String::toUpperCase).
        collect(Collectors.toList());
    
    • 1
    • 2
    • 3

    ** flatMap(Function fun) 层级结构扁平化,汇总成流**

    Stream> inputStream = Stream.of(
         Arrays.asList(1),
         Arrays.asList(2, 3),Arrays.asList(4, 5, 6)
     );
    Stream outputStream = inputStream.
        flatMap((childList) -> childList.stream());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    filter(Predict predict) 将predict.test(T t)测试为true的留下形成新stream

    List output = reader.lines().
         flatMap(line -> Stream.of(line.split(REGEXP))).
    filter(word -> word.length() > 0).
         collect(Collectors.toList());
    
    • 1
    • 2
    • 3
    • 4

    (3)终结操作(Terminal)常见的终结操作主要有:

    forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator。

    示例:

    forEach(Consumer consumer)遍历操作元素

    roster.stream()
     .filter(p -> p.getGender() == Person.Sex.MALE)
     .forEach(p -> System.out.println(p.getName()));
    
    • 1
    • 2
    • 3

    注意:由于Stream是Terminal操作,所以不可以在一个Stream中使用两次Terminal操作

    stream.forEach(element -> doOneThing(element));
    stream.forEach(element -> doAnotherThing(element));
    
    • 1
    • 2

    相应的,有peek(Consumer consumer)来进行类似操作,因为peek属于Intermediate操作,不会终止流。

    Stream.of("one", "two", "three", "four")
      .filter(e -> e.length() > 3)
      .peek(e -> System.out.println("Filtered value: " + e))
      .map(String::toUpperCase)
      .peek(e -> System.out.println("Mapped value: " + e))
      .collect(Collectors.toList());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里应该注意的是,程序的输出结果是:

    Filtered value: three
    Mapped value: THREE
    Filtered value: four
    Mapped value: FOUR
    
    • 1
    • 2
    • 3
    • 4

    所以,从这里可以看到Stream对元素的执行顺序是每一个元素依次执行,而不是每一阶段的流向下整体执行

    Option findFirst()返回流中第一个元素,属于Terminal兼short-circuiting操作

    public static int getNum(){
        Random random = new Random();
        Stream stream3 = Stream.generate(random::nextInt);
        //stream3.findFirst().ifPresent(System.out::println);
        return stream3.limit(0).findFirst().orElse(-1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里需要注意的是,对于此类操作,返回的是Optional包装类型,有对应的处理方法如orElse(T t)、ifPresent(Consumer consumer)等。在程序逻辑中可以在一定程度上防止NPE问题。类似的操作还有:findAny、max/min、reduce等。

    T reduce(T identity, BinaryOperator accumulator) 将元素进行迭代结合,其实max/min、sum、average都属于封装的reduce操作

    Random random = new Random();
    Stream stream3 = Stream.generate(random::nextInt);
    //注意加不加identity初始值的区别,很合理
    Optional reduce = stream3.limit(10).reduce(Integer::sum);
    Integer sum = stream3.limit(10).reduce(0, Integer::sum);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    limit/skip(Long n) 限定流条件

    Random random = new Random();
    Stream stream3 = Stream.generate(random::nextInt);
    int num = stream3.limit(10).skip(3).collect(Collectors.toList()).size();
    System.out.println(num);
    
    • 1
    • 2
    • 3
    • 4

    sorted(Comparator comparator)对元素进行排序

    Random random = new Random();
    Stream stream3 = Stream.generate(random::nextInt);
    stream3.limit(10).sorted((o1, o2) -> o2-o1).forEach(System.out::println);
    
    • 1
    • 2
    • 3

    min/max/distinct()

    Random random = new Random();
    Stream stream3 = Stream.generate(random::nextInt);
    stream3.limit(10).distinct().max((o1, o2) -> o1-o2).ifPresent(System.out::println);
    
    br.lines().flatMap(line -> Stream.of(line.split(" ")))
      .filter(word -> word.length() > 0)
      .map(String::toLowerCase)
      .distinct()
      .sorted()
      .collect(Collectors.toList());
    br.close();
    System.out.println(words);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    (4)短路操作(short-ciecuiting)

    常见的短路操作有:

    anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit示例:

    Random random = new Random();
    Stream stream3 = Stream.generate(random::nextInt);
    boolean b = stream3.limit(10).noneMatch(val -> val > 0);
    System.out.println(b);
    
    • 1
    • 2
    • 3
    • 4

    (5)利用Collectors进行分组操作(groupingBy/partitioningBy)

    java.util.stream.Collectors 类的主要作用就是辅助进行各类有用的 reduction 操作,例如转变输出为 Collection,把 Stream 元素进行归组。

    Collectors.groupingBy(Function classifier)按照Function条件进行分组

    Map> personGroups = Stream.generate(new PersonSupplier()).
         limit(100).
         collect(Collectors.groupingBy(Person::getAge));
    Iterator it = personGroups.entrySet().iterator();
    while (it.hasNext()) {
         Map.Entry> persons = (Map.Entry) it.next();
         System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Collectors.partitioningBy(Predicate predicate) 按照predict条件分为两组

    Random random = new Random();
    Stream stream3 = Stream.generate(random::nextInt);
    Map> collect = stream3.limit(10).collect(Collectors.partitioningBy(val -> val > 0));
    Set keySet = collect.keySet();
    keySet.forEach(item->collect.get(item).forEach(System.out::println));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上,多线程对应的处理方法如parallelStream()进行处理,提高效率,但是并行处理不能保证元素有序,还应注意线程安全问题。

  • 相关阅读:
    面试必知的9个性能测试指标,你完全了解吗?
    如何使用ps制作ico图标文件
    QLineEdit设置数据的输入范围QIntValidator和QDoubleValidator
    Ubuntu MongoDB账户密码设置
    JS元编程
    [OpenJDK:环境变量配置]:填充Profile并修改默认配置
    [附源码]Python计算机毕业设计SSM泸定中学宿舍管理系统设计(程序+LW)
    如何使用 LinkedHashMap 实现 LRU 缓存?
    剑指offer62-67排序-前缀树
    2023-09-19 LeetCode每日一题(打家劫舍 IV)
  • 原文地址:https://blog.csdn.net/feifei000131/article/details/134079390