Java8——lambda表达式 + stream流

  • 什么是lambda表达式
  • 语法格式
  • 函数式接口
  • 四大内置核心函数式接口
    • Consumer
    • Supplier
    • Function
    • Predicate
  • 引用
    • 方法引用
    • 构造器引用
    • 数组引用
  • stream流

1. 什么是lambda表达式

  • 匿名函数
  • 更紧凑,简洁的代码风格

(1)原先的写法——匿名内部类

public void test(){
    //匿名内部类
    Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return Integer.compare(o1,o2);
        }
    };
    //调用
    TreeSet<Integer> set = new TreeSet<>(comparator);
}

(2)Lambda的写法

public void test(){
    // Lambda 表达式
    Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
    //调用
    TreeSet<Integer> set = new TreeSet<>(comparator);
}

2. lambda表达式的写法

(1)无参数,无返回值

//1.原先写法
Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
};
-----------------------------------------------------
//2.lambda写法
Runnable run = () -> { System.out.println("Hello World");}

(2)有参数,无返回值

//1 该lambda方法定义了一个函数:打印x
Consumer<String> consumer = (x) -> { System.out.println(x); }
//2 执行该函数,x="打印"
consumer.accept("打印");

(3)有参数,有返回值

//1. 该lambda方法定义了一个比较器
Comparator<Integer> comparator = (a, b) -> {
    System.out.println("比较接口");
    return Integer.compare(a, b);
};
//2. 如果只有一条执行语句,return可以不写
Comparator<Integer> comparator = (a, b) -> { Integer.compare(a, b); };

(4)注意事项

  • 若lambda体中只有一条语句,return可以不写
  • lambda参数列表的数据类型可以省略不写,因为JVM编译器会自动推断
    • eg. (Integer a, Integer b) ->{ ... } 等价于 (a, b) -> { ... }

3. 如何定义函数式接口

  • 两点
    • 1. 编写一个接口,加上@FunctionalIterface的注解
    • 2. 接口里只能写一个抽象方法

(1)定义一个函数式接口

@FunctionalInterface
public interface MyFun {
    //定义了一个count函数
    Integer count(Integer a, Integer b);
}

(2)使用

public void test(){
    //定义count函数
    MyFun myFun1 = (a, b) -> a + b;
    //执行函数
    int x = myFun1.count(1, 2);
    //结果为 1+2 = 3
    System.out.println(x);
}

(3)更高端点的用法

public Integer operation(Integer a, Integer b, MyFun myFun){
    return myFun.count(a, b);
}

public void test(){
    Integer result = operation(1, 2, (x, y) -> x + y);
    System.out.println(result);
}

4. 四大内置核心函数式接口

  • 实际开发中,很少需要自定义函数式接口,JDK已经提供
函数式接口参数类型返回类型用途
消费型接口:ConsumerTvoid对类型为T的对象应用操作:void accept(T t)
提供型接口:SupplierT返回类型为T的对象:T get()
函数型接口:Function<T, R>TR对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t)
断言型接口:PredicateTboolean确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t)

(1)Consumer

  • 源码
@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
  • 用法
//1. accept()方法
public void test(){
    //定义函数
    Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
    //输出结果为 ”消费型接口100“
    consumer.accept(100);
}

//2. andThen()方法,
public static void main(String[] args) {
    //consumer1函数
    Consumer<Integer> consumer1 = (x) -> {
        System.out.println(String.valueOf(x+"_"));
    };
    //consumer2函数
    Consumer<Integer> consumer2 = (y) -> {
        System.out.println(String.valueOf(y * 2));
    };
    //comsumer3函数:先执行consumer1,接着执行consumer2
    Consumer<Integer> comsumer3 = consumer1.andThen(consumer2);
    //结果为1_2
    comsumer3.accept(1);
}

(2)Supplier

  • 源码
@FunctionalInterface
public interface Supplier<T> {

    T get();
}
  • 用法
private static void testSupplier(){
    //1.1 无需参数,有返回值
    Supplier<Integer> sup1 = () -> { return 1; };
    //1.2 打印1
    System.out.println(sup1.get());
        
    //2.1 调用String的无参构造器
    Supplier<String> sup2 = String::new;
    //2.2 打印空字符串“”
    System.out.println(sup2.get());

    //3.1 
    Supplier<String> sup3 = ()->{ return new String("123"); };
    //3.2 打印字符串“123”
    System.out.println(sup3.get());
}

(3)Function<T, R>

  • 源码
@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    //返回一个先执行before函数对象apply方法再执行当前函数对象apply方法的函数对象
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    //返回一个先执行当前函数对象apply方法再执行after函数对象apply方法的函数对象。
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
  • 用法
//Function函数
Function<String, String> function = a -> a + " Jack!";
System.out.println(function.apply("Hello")); // Hello Jack!
//andThen()
Function<String, String> function = a -> a + " Jack!";
Function<String, String> function1 = a -> a + " Bob!";
String greet = function.andThen(function1).apply("Hello");
System.out.println(greet); // Hello Jack! Bob!
//compose()
Function<String, String> function = a -> a + " Jack!";
Function<String, String> function1 = a -> a + " Bob!";
String greet = function.compose(function1).apply("Hello");
System.out.println(greet); // Hello Bob! Jack!

(4)Predicate

  • 源码
@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    //与操作
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
    //取反操作
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    //或操作
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    //是否相等且非空
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
  • 用法
private static void testSupplier(){
    Integer age = 35;
    String gender = "男";
    //是否大于等于35岁
    Predicate<Integer> predicate1 = (i) -> i >= 35;
    //是否是男性
    Predicate<Integer> predicate2 = (i) -> i.equals("男");
    
    //返回false
    Boolean b1 = predicate1.test(25);
    //返回true
    Boolean b2 = predicate2.test(“男”)
}

(5)其他常用函数式接口

函数类型函数名作用
Consumer
  • Consumer<T>
  • BiConsumer<T,U>
  • 提供一个T类型的输入参数,不返回执行结果
  • 提供两个自定义类型的输入参数,不返回执行结果
  • DoubleConsumer
  • IntConsumer
  • LongConsumer
  • 表示接受单个double / int / long值参数,但不返回结果的操作
  • ObjDoubleConsumer<T>
  • ObjIntConsumer<T>
  • ObjLongConsumer<T>
  • 表示接受object值和double / int / long值,但是不返回任何操作结果
Supplier
  • Supplier<T>
  • 不提供输入参数,但是返回结果的函数
  • BooleanSupplier
  • DoubleSupplier
  • IntSupplier
  • LongSupplier
  • 不提供输入参数,但是返回boolean / double / int / long结果的函数
Predicate
  • Predicate<T>
  • BiPredicate<T,U>
  • 对给定的输入参数执行操作,返回一个boolean类型的结果(布尔值函数)
  • 对给定的两个输入参数执行操作,返回一个boolean类型的结果(布尔值函数)
  • DoublePredicate
  • IntPredicate
  • LongPredicate
  • 对给定的double / int / long参数执行操作,返回一个boolean类型的结果(布尔值函数)
Function
  • Function<T,R>
  • BiFunction<T,U,R>
  • 接收一个参数并返回结果的函数
  • 接受两个参数并返回结果的函数
  • DoubleFunction<R>
  • DoubleToIntFunction
  • DoubleToLongFunction
  • 接收一个double类型的参数并返回结果 R / int / long 的函数
  • IntFunction<R>
  • IntToDoubleFunction
  • IntToLongFunction
  • 接收一个int类型的参数并返回结果 R / double / long 的函数
  • LongFunction<R>
  • LongToDoubleFunction
  • LongToIntFunction
  • 接收一个long类型的参数并返回结果 R / double / int 的函数
  • ToDoubleBiFunction<T,U>
  • ToIntBiFunction<T,U>
  • ToLongBiFunction<T,U>
  • 接收两个参数并返回 double / int / long 结果的函数
  • ToDoubleFunction<T>
  • ToIntFunction<T>
  • ToLongFunction<T>
  • 接收一个参数并返回 double / int / long 结果的函数
Operation
  • UnaryOperator<T>
  • BinaryOperator<T>
  • 提供单个参数,并且返回一个与输入参数类型一致的结果
  • 提供两个个参数,并且返回一个与输入参数类型一致的结果
  • DoubleUnaryOperator
  • IntUnaryOperator
  • LongUnaryOperator
  • 提供单个double / int / long 参数并且返回int结果
  • DoubleBinaryOperator
  • IntBinaryOperator
  • LongBinaryOperator
  • 提供两个double / int / long 参数并且返回double结果

5. 引用

  • 若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”

(1)方法引用

  • 对象 :: 实例方法
//1. 对象 :: 实例方法
public void test(){
    PrintStream ps = System.out;
    //传统lambda函数写法
    Consumer<String> con1 = (s) -> ps.println(s);
    con1.accept("aaa");
    //方法引用的写法
    Consumer<String> con2 = ps::println;
    con2.accept("bbb");
}
  • 类 :: 静态方法
//2. 类 :: 静态方法
public void test(){
    Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
    System.out.println(com1.compare(1, 2));

    Comparator<Integer> com2 = Integer::compare;
    System.out.println(com2.compare(2, 1));
}
  • 类 :: 实例方法
//3. 类 :: 实例方法
public void test03(){
    BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
    System.out.println(bp1.test("a","b"));

    BiPredicate<String, String> bp2 = String::equals;
    System.out.println(bp2.test("c","c"));
}

(2)构造器引用

@Test
public void test04(){
    Supplier<List> sup1 = () -> new ArrayList();

    Supplier<List> sup2 = ArrayList::new;
}

(3)数组引用

Type :: new


6. Stream API

(1)什么是流(Stream)

  • 用于操作数据源(集合,数组等)所生成的元素序列
  • 注意事项
    • Stream不会自己存储元素
    • Stream不会改变源对象,它们会返回一个持有结果的新stream
    • Stream是延迟执行的,这意味着它们会等到需要结果时才执行

(2)Stream的三个步骤

  • 1. 创建Stream
    • 集合/数组 .stream()方法,生成一个流
  • 2. 中间操作
    • 比如 filter(), limit(), distinct()等中间链操作
  • 3. 终止操作

(3)创建流的几种方法

public void test(){
    //集合流
    //Collection.stream() 穿行流
    //Collection.parallelStream() 并行流    
    List<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();
    //数组流
    //Arrays.stream(array)
    String[] strings = new String[10];
    Stream<String> stream2 = Arrays.stream(strings);
    //Stream 静态方法
    //Stream.of(...)
    Stream<Integer> stream3 = Stream.of(1, 2, 3);
    //无限流
    //迭代
    Stream<Integer> stream4 = Stream.iterate(0, (i) -> ++i+i++);
    stream4.forEach(System.out::println);
    //生成
    Stream.generate(() -> Math.random())
        .limit(5)
        .forEach(System.out::println);
}

7. stream流的中间操作

(1)筛选 / 切片

  • filter(Function):从流中排除某些元素
  • limit(n):截断流,只取流的前n个元素
  • skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
  • distinct():筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
//构造数据
List<Employee> emps = Arrays.asList(
    new Employee(101, "Z3", 19, 9999.99),
    new Employee(102, "L4", 20, 7777.77),
    new Employee(103, "W5", 35, 6666.66),
    new Employee(104, "Tom", 44, 1111.11),
    new Employee(105, "Jerry", 60, 4444.44)
);

public void test01(){
    emps.stream()
        .filter((x) -> x.getAge() > 35)
        .limit(3) 
        .distinct()
        .skip(1)
        .forEach(System.out::println);
}

(2)映射

  • map(Function):映射,接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
  • flatMap(Function):接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
  • 注:map和flatMap之间的关系可以类比为 add() 和 addAll()之间的关系
//map
public void test02(){
    //case1
    List<String> list = Arrays.asList("aaa", "bbb", "ccc");
    Stream<String> stringStream = list.stream()
                                      .map((str) -> str.toUpperCase());
    stringStream.forEach(System.out::println);
    //结果为“AAA”,"BBB","CCC"

    //case2
    List<Employee> employees;
    employees.stream()
             .map(Employee::getName)
             .foreach(System.out::println);
}
//flatMap的结果
public void test02(){
    String[] words = new String[]{"Hello","World"};
    List<String> b = words.stream()
                          .map(word -> word.split(""))
                          .flatMap(Arrays::stream)
                          .distinct()
                          .collect(toList());
    b.forEach(System.out::print);//结果为HeloWrd
}

(3)排序

  • sorted():自然排序(根据Comparable的compareTo方法)
    • 比如String类型已经实现了该方法,默认按照字典序
  • sorted(Comparator c):定制排序
public void test03(){
    List<Integer> list = Arrays.asList(5,2,2,4,5);
//1. sorted()
    list.stream()
        .sorted() //comparaTo()
        .forEach(System.out::println);//22455
//2. sorted(Comparator c)
    list.stream()
        .sorted((e1, e2) -> {e1 - e2;}})
        .forEach(System.out::println);
}

(4)匹配

  • allMatch(Predicate):检查该Predicate是否满足所有元素,如果不是返回False
  • anyMatch(Predicate):检查是否至少匹配一个元素
  • noneMatch(Predicate):检查是否没有匹配所有元素
public void test04(){
//allMatch
    List<Integer> list = Arrays.asList(1, 2, 3 ,4 ,5 ,6);
    boolean answer = list.stream()
                         .allMatch(a -> a > 3);
    System.out.println(answer);    //false
}

(5)查找与计算

  • findFirst:返回流中第一个元素,返回类型为Optional对象
  • findAny:返回当前流中的任意元素
  • 注:注意parallelStream()和steam()的区别
public void test05(){
//findFirst和findAny的区别
    List<String> lst1 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
    List<String> lst2 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");

    Optional<String> findFirst = lst1.stream().filter(s -> s.startsWith("D")).findFirst(); 
    Optional<String> findParallelFirst = lst1.parallelStream().filter(s ->     s.startsWith("D")).findFirst();
    Optional<String> findAny = lst2.stream().filter(s -> s.startsWith("J")).findAny();
    Optional<String> findParallelAny = lst2.parallelStream().filter(s -> s.startsWith("J")).findAny();
 
    System.out.println(findFirst.get()); //总是打印出David
    System.out.println(findParallelFirst.get()); //总是打印出David
    System.out.println(findAny.get()); //总是打印出David
    System.out.println(findParallelAny.get()); //会随机地打印出Jack/Jill/Julia
}

(6)统计

  • count:返回流中元素的总个数,返回流中的数量,返回Long
  • max() / max(Comparator c):返回流中最大值,不传Comparator,默认compareTo()
  • min() / min(Comparator c):返回流中最小值,不传Comparator,默认compareTo()
public void test06(){
    List<Integer> list = Arrays.asList(3, 2, 1, 6, 5, 4);
    int max = list.stream().max((a, b) -> a > b ? 1 : -1).get();    //max=6
    int max = list.stream().min((a, b) -> a > b ? 1 : -1).get();    //min=6
}

(7)归约

  • reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
public void test07(){
//reduce求对Integer和
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    Integer sum = list.stream()
                      .reduce(0, (x, y) -> x + y);
    System.out.println(sum);    //1+2+...+8+9=45
//reduce求对某个属性求和,返回Optional对象,因为Salary对象可能为null
    Optional<Double> op = employees.stream()
                                   .map(Employee::getSalary)
                                   .reduce(Double::sum);
}

(8)收集:主要配Collectors工具类使用,Collectors工具类中提供了很多常用的静态方法,用于方便地创建常用收集器实例

  • collect(Collector c):collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法

(9)Collections工具类

  • 1. 创建容器
    • Collectors.toList()
    • Collectors.toSet()
    • Collectors.toMap()
    • Collectors.toCurrentMap()
    • 定制特定的容器:例如创建LinkedHashSet:Collectors.toCollection(LinkedHashSet::new)
public void test09_1(){
//放入List
    List<String> list = emps.stream()
                            .map(Employee::getName)
                            .collect(Collectors.toList());     
//放入Set
    Set<String> set = emps.stream()
                          .map(Employee::getName)
                          .collect(Collectors.toSet());
//放入LinkedHashSet
    LinkedHashSet<String> linkedHashSet = emps.stream()
                          .map(Employee::getName)
                          .collect(Collectors.toCollection(LinkedHashSet::new));
}
  • 2. 统计相关
    • Collectors.counting():返回总数 Long
    • Collectors.averagingInt / Double / Long():返回平均值 Int / Double / Long
    • Collectors.summingInt / Double / Long():返回总和 Int / Double / Long
    • Collectors.maxBy / minBy(Comparator):排序后取最大 / 最小
public void test09_2(){
//总数
    Long count = emps.stream()
                     .collect(Collectors.counting());
//平均值
    Double avg = emps.stream()
                 .collect(Collectors.averagingDouble(Employee::getSalary));
//总和
    Double sum = emps.stream()
                 .collect(Collectors.summingDouble(Employee::getSalary));
//最大值/最小值
    Optional<Employee> max = emps.stream()
          .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
}
  • 3. 分组
    • Collectors.groupBy(Function):按照某个规则分组,返回Map<分组规则,List<>>类型
    • 多级分组:Collectors.groupBy(Function,Collectors.groupBy(Function)):先按括号里面的规则分组,分组完继续分组
public void test09_3(){
//1. 普通分组,根据Id分组
    Map<Integer, List<Employee>> map = emps.stream()
        .collect(Collectors.groupingBy(Employee::getId));

//2. 多级分组
    Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
        .collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
            if (e.getAge() > 35) {
                return "开除";
            } else {
                return "继续加班";
            }
        })));
}
  • 4. 分区:分成两个区,满足条件的true和不满足条件的false
    • Collectors.partitioningBy(Function):返回Map<Boolean, List<>>,满足条件的和不满足条件的
public void test09_4(){
//分区,返回两个组
    Map<Boolean, List<Employee>> listMap = emps.stream()
        .collect(Collectors.partitioningBy((e) -> e.getSalary() > 10000));
}
  • 5. SummaryStatistics类
    • DoubleSummaryStatistics / Int... / Long... 类:详细用法见用例
public void test09_5(){
    DoubleSummaryStatistics dss = emps.stream()
        .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dss.getMax());
    System.out.println(dss.getMin());
    System.out.println(dss.getSum());
    System.out.println(dss.getCount());
    System.out.println(dss.getAverage());
}
  • 6. 连接
    • Collectors.joining(Function):用于将元素连接之后返回字符串
public void test09_6(){
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
    String str = list.stream()
                     .map(String::valueOf)
                     .collect(Collectors.joining("-"));
    System.out.println(str);    //1-2-3-4-5-6
}

8. 串行流与并行流

(1)串行流与并行流

  • stream():串行流
  • parallelStream():并行流,底层运用ForkJoinPool线程池实现

(2)如何相互转换

  • 串 —> 并:parallel()方法,parallelStream()等价于 stream().parallel()
  • 并 —> 串:sequential()方法,stream()等价于 parallelStream().sequential() 

(3)forEach() 和 forEachOrdered()

  • 对于 stream() 流而言
    • stream().forEach() 和 stream.forEachOrdered() 没有区别,因为都是串行流,都是严格按照集合的顺序执行的
  • 对于 parallelStream() 流而言
    • forEach()不保证先后顺序,因为是并发执行
    • forEachOrdered() 保证先后顺序,其内部采用多线程同步的方式运行,底层采用Fork/Join框架实现
  • 效率比较
    • 【for循环】,【stream().forEach()】,【parallelStream.forEachOrdered()】三者效率没有太大差异
    • 但【parallelStream(). forEachOrdered()】由于是多线程,与for、stream两种单线程的方式相比,优势在于很好的利用了CPU多核的资源
    • 【parallelStream().forEach()】显然效率最高,因为是并行的,但是缺点是不保证前后顺序,需要考虑线程安全问题

9. Optional类

(1)概念

Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;并且可以避免空指针异常

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值