Java8新特性之lambda表达式和Stream

1. Lambda表达式

1.1 含义

百度百科

链接

lambda expression是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数 。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)

java官方例子

链接:https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

个人理解

在java中的lambda表达式可以看做是一个匿名方法,但java中并没有匿名方法这一说法,只有匿名类,当匿名类实现的接口只有一个抽象方法时,那么这个匿名类就可以写作lambda表达式,所以lambda表达式实际上是一个接口的匿名实现类

1.2 @FunctionalInterface

含义

@FunctionalInterface是一个注解,用于标记一个接口是函数式接口,表明这个接口只有一个抽象方法,多个的情况下编译器会报错。

满足条件

满足加注解的条件就是看接口中抽象方法的数量,必须是1个。

接口中的方法都是没有abstract关键字修饰的抽象方法,但是从java8开始中接口是可以有default方法和static方法的。所以这两种是不计入数量的。

还有另一种情况,接口中和Object中同名的方法也是不计入的。

作用

函数式接口的实现可以用lambda表示。其实当一个接口满足加@FunctionalInterface的条件,但没有加时,也可以用lambda表示。

例子
    @FunctionalInterface
    public interface MyFunction {
        void test();
        String toString();
    }
    public interface MyFunction2 {
            void test();
            String toString();
    }
    public static void main(String[] args) {
    	MyFunction m = () -> {};
        MyFunction2 m2 = () -> {};
    }

1.3 基本语法

格式
  • (parameters) -> expression
  • (parameters) ->{ statements; }
含义及解释
  • parameters是参数列表,参数的类型可以不指定,编译器会自动推断。
  • parameters只有一个参数时,可以不用()包起来,如果没有参数或多个时,则必须用()包起来。
  • expression是一个表达式,可以不用{}包裹,statements是语句列表,如果只包含1条时,也可以不用{}。
例子
    public interface M1 {
    	void test(int i);
    }
    public interface M2 {
    	int test(int i);
    }
    M1 m1 = (a) -> {System.out.println(a);};
    M1 m11 = a -> {System.out.println(a);};
    M1 m12 = a -> System.out.println(a);
    M1 m13 = a -> {System.out.println(a);System.out.println(a);};

2. 方法引用

2.1 含义

java8新增::符号用于表示方法引用, 是对lambda表达式更简洁的一种写法,是lambda表达式的一种语法糖。

2.2 语法

类型语法等价的Lambda表达式
构建方法引用ClassName::new() -> new ClassName()
实例方法引用instance::instanceMethod() -> instance.instanceMethod()
静态方法引用ClassName::staticMethod() -> ClassName.staticMethod()
对象方法引用ClassName::instanceMethod(arg1,arg2[,…]) -> arg1.instanceMethod(arg2[,…])

2.3 例子

    public class Student {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public static Student getInstance() {
            return new Student();
        }
    
        public Student get() {
            return new Student();
        }
    
        public int compare(Student student) {
            return this.name.compareToIgnoreCase(student.name);
        }
    }
    @FunctionalInterface
    public interface MyFunction {
        Student get();
    }
    public interface MyFunction2 {
        int compare(Student s1, Student s2);
    }
    public static void main(String[] args) {
    		
        	// 等价于 MyFunction m1 = () -> new Student();
            MyFunction m1 = Student::new;
        	
    		// 等价于 MyFunction m12 = () -> Student.getInstance();
            MyFunction m12 = Student::getInstance;
    		
            Student student = new Student();
    		// 等价于 MyFunction m13 = () -> student.get();
            MyFunction m13 = student::get;
    		
            // 等价于 MyFunction2 m2 = (ma, mb) -> ma.compare(mb);
            MyFunction2 m2 = Student::compare;
    
            String[] stringArray = { "Barbara", "James", "Mary", "John",
                    "Patricia", "Robert", "Michael", "Linda" };
            Arrays.sort(stringArray, String::compareToIgnoreCase);
    }

3 几个重要的函数式接口

这几个接口都位于java.util.function包下面,是java官方提供、很常用的函数式接口,对理解Stream API很有用处

3.1 Consumer

    @FunctionalInterface
    public interface Consumer<T> {
    	// 接收一个参数,没有输出
        void accept(T t);
    	// 先执行this, 再执行after
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
    Consumer<String> strPrintln = System.out::println;
    strPrintln.accept("1");
    strPrintln.andThen(strPrintln).accept("hello");

3.2 Function

Stream中使用map的参数就是Function

    @FunctionalInterface
    public interface Function<T, R> {
    	// 输入一个参数,输出一个参数
        R apply(T t);
        
    	// 先执行before,再执行this
        default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
        
    	// 先执行this,再执行after
        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<Integer, Integer> f = a -> a * a;
    Function<Integer, Integer> f1 = a -> a + a;
    
    System.out.println(f.apply(1)); 
    System.out.println(f.compose(f1).apply(1));
    System.out.println(f.andThen(f1).apply(1));
    // 输出 
    // 1
    // 4
    // 2

3.3 Predicate

Stream中使用filter的参数就是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);
        }
    }
    
    Predicate<String> p = str -> str.length() >= 5;
    Predicate<String> p1 = str -> str.length() <= 10;
    
    String testWord = "HelloWorld";
    System.out.println(p.test(testWord));
    System.out.println(p.and(p1).test(testWord));
    System.out.println(p.negate().test(testWord));
    System.out.println(p.or(p1).test(testWord));
    
    // 输出 
    // true
    // true
    // false
    // true

3.4 Supplier

    @FunctionalInterface
    public interface Supplier<T> {
    
    	// 不接受参数,返回1个结果,常用于工厂方法
        T get();
    }
    Supplier<LocalDate> date = LocalDate::now;
    System.out.println(date.get());

4. Stream

Stream是对集合(Collection)对象功能的增强

4.1 Api

  • allMatch

    集合转化为流,流中所有元素都要匹配。传入参数Predicate

    String[] stringArray = { "Barbara", "James", "Mary", "John",
                    "Patricia", "Robert", "Michael", "Linda" };
    
    boolean b = Arrays.stream(stringArray).allMatch(str -> str.length() > 8 && str.length() < 20);
    System.out.println(b);
    
    Predicate<String> p1 = str -> str.length() > 8;
    Predicate<String> p2 = str -> str.length() < 20;
    boolean c = Arrays.stream(stringArray).allMatch(p1.and(p2));
    System.out.println(c);
    
  • anyMatch

    流中任一元素匹配就返回true,否则false。传入参数Predicate

    Stream<String> stream = Arrays.stream(stringArray);
    boolean b = stream.anyMatch(str -> str.length() > 10);
    System.out.println(b);
    
  • collect

    将流中元素收集起来。传入参数Collector,返回集合

    List<String> stringList = Arrays.stream(stringArray).collect(Collectors.toList());
    System.out.println(stringList);
    Set<String> stringSet = Arrays.stream(stringArray).collect(Collectors.toSet());
    System.out.println(stringSet);
    
  • concat

    静态方法,将两个流连接为一个流

    Stream<String> concat = Stream.concat(Arrays.stream(stringArray), Arrays.stream(stringArray));
    
  • count

    计算流中元素的个数

    long count = Arrays.stream(stringArray).count();
    
  • distinct

    对流中的元素去重,使用 Object.equals方法

    long count = Arrays.stream(stringArray).distinct().count();
    
  • filter

    过滤容器中的元素,只保留函数返回true的元素。参数为Predicate,返回Stream

    List<String> stringList = Arrays.stream(stringArray).filter(str -> str.length() > 8 && str.length() < 20).collect(Collectors.toList());
    
  • findAny

    流中任一元素匹配就返回它,多次调用可能不是同一个结果,是不稳定的

    Optional<String> any = Arrays.stream(stringArray).findAny();
    any.ifPresent(System.out::println);
    
  • findFirst

    流第一个匹配的元素,多次调用返回同一个结果,是稳定的

    Optional<String> any = Arrays.stream(stringArray).findFirst();
    any.ifPresent(System.out::println);
    
  • flatMap

    map的扁平化操作,将流中每个元素转化为流,并合并到一起

    Arrays.stream(stringArray).flatMap(str -> Stream.of(str.split(""))).collect(Collectors.toList()).forEach(System.out::println);
    
  • forEach

    对流中的每个元素进行操作,是没有输出的,传入一个Consumer.在并行流中不能保证元素顺序

    Arrays.stream(stringArray).collect(Collectors.toList()).forEach(System.out::println);
    
  • forEachOrdered

    和forEach类似,不过在并行流中能保证顺序

  • generate

    静态方法,生成1个无限无序的流,常和limit用来组合

    Stream.generate(() -> 0).limit(5).forEach(System.out::println);
    Stream.generate(new Random()::nextInt).limit(5).forEach(System.out::println);
    Stream.generate(() -> new Random().nextInt(5)).limit(5).forEach(System.out::println);
    
  • iterate

    静态方法,生成1个无限有序的流,常和limit用来组合

    Stream.iterate(0, n -> n + 2).limit(5).forEach(System.out::println);
    Stream.iterate("0", n -> n + "0").limit(5).forEach(System.out::println);
    
  • limit

    限制流中元素个数

  • map

    将集合中的元素转化为另一种类型的元素,输入参数为Function

    Arrays.stream(stringArray).map(String::length).forEach(System.out::println);
    
  • mapToInt

    和map类似,如果使用map返回的类型是Integer, 后续操作需要拆装箱,可以使用这个来提高性能

    List<Integer> list = Arrays.asList(1,2,3,4);
    // 流中的元素都是对象,这里对List中Integer进行加法计算,需要频繁拆箱和装箱
    int sum1 = list.stream().reduce(0,(acc,e) -> acc + e).intValue();
    // 使用mapToInt可以减少拆装箱次数,提高性能
    int sum2 = list.stream().mapToInt(e -> e).sum();
    
  • max

    返回流中最大值, 可传入一个Comparator

    Arrays.stream(stringArray).max(String::compareTo).ifPresent(System.out::println);     Arrays.stream(stringArray).max(Comparator.comparingInt(String::length).reversed()).ifPresent(System.out::println);
    
  • min

    返回流中最小值

  • noneMatch

    对流中元素进行匹配,都不满足返回true,空流也返回true

    boolean n = Arrays.stream(stringArray).noneMatch(s -> s.length() > 20);
    boolean empty = Arrays.stream(stringArray).filter(s -> s.charAt(0) == ' ').noneMatch(s -> s.length() > 20);
    
  • of

    静态方法,将数组组装为Stream

  • peek

    peek返回流中的元素,额外的操作就是对流中元素执行传入的函数。传入参数Consumer

    这个方法的存在主要是为了支持调试,元素流经管道中的某个点时能看到

    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()).forEach(System.out::println);
    
  • reduce

    对流中元素进行reduce(减少,缩小)操作,将所有元素处理得到唯一值。reduce有三个方法,从以下三个例子可以看出reduce的用法。

    1. Optional<T> reduce(BinaryOperator<T> accumulator)

      // 累加,由于流可能为空,最后会得到一个Optional
      Optional<Integer> resultOptional = Stream.of(1, 2, 3).reduce((acc, e) -> acc + e);
      
      // 等价于
      boolean foundAny = false;
      Integer result = null;
      int [] array = {1, 2, 3};
      BinaryOperator<Integer> accumulator = (acc, e) -> acc + e;
      for (Integer e : array) {
      	if (!foundAny) {
      		foundAny = true;
      		result = e;
      	}
      	else
      		result = accumulator.apply(result, e);
      }
      Optional<Integer> resultOptional = foundAny ? Optional.of(result) : Optional.empty();
      
    2. T reduce(T identity, BinaryOperator<T> accumulator)

      // 同样是累加,存在初始值
      int result = Stream.of(1, 2, 3).reduce(0, (acc, e) -> acc + e);
      
      // 等价于
      int [] array = {1, 2, 3};
      int identity = 0;
      BinaryOperator<Integer> accumulator = (acc, e) -> acc + e;
      int result = identity;
      for (Integer e : array)
      	result = accumulator.apply(result, e);
      
    3. <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)

      combiner在并行流的情况下生效,将不同线程操作的结果汇总在一起。其余和两个参数的reduce方法一致

      int result = Stream.of(1, 2, 3).parallel().reduce(0, Integer::sum, (a, b) -> {System.out.println("parallel combiner invoke"); return a + b;});
      int other = Stream.of(1, 2, 3).reduce(0, Integer::sum, (a, b) -> {System.out.println("combiner invoke"); return a + b;});
      
  • skip

    丢弃流中前n个元素

    Arrays.stream(stringArray).skip(3L).collect(Collectors.toList()).forEach(System.out::println);
    
  • sorted

    对流中元素进行排序

    Arrays.stream(stringArray).sorted().collect(Collectors.toList()).forEach(System.out::println);
    Arrays.stream(stringArray).sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList()).forEach(System.out::println);
    
  • toArray

    将流中元素转化为一个数组

  • collect

    1. Collector

      在流中的数据经常要使用collect方法收集起来,传递给collect的参数是Collector。jdk提供了一些常用的实现在Collectors类中,比如Collectors.toList()、Collectors.toSet()。

      public static <T> Collector<T, ?, List<T>> toList() {
      	return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                         (left, right) -> { left.addAll(right); return left; },
                                         CH_ID);
      }
      
    2. collect

      collect还有个方法,提供和reduce类似的三个参数

      <R> R collect(Supplier<R> supplier,
                        BiConsumer<R, ? super T> accumulator,
                        BiConsumer<R, R> combiner);
      
      • supplier 创建一个新容器,并行可能会创建多次,并且每次不同对象
      • accumulator 将元素添加进结果集中
      • combiner 在并行计算时汇总不同线程计算的结果。它的输入是两个结果集,必须将第二个结果集中的值全部放入第一个结果集中
      List<String> asList = Arrays.stream(stringArray).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
      
      List<String> asList1 = Arrays.stream(stringArray).collect(() -> {System.out.println("new invoke"); return new ArrayList<>();}, 
                      ArrayList::add, 
                      (a, b) -> {System.out.println("invoke");});
      
      List<String> asList2 = Arrays.stream(stringArray).parallel().collect(() -> {System.out.println("new invoke"); return new ArrayList<>();}, 
                      ArrayList::add, 
                      (a, b) -> {System.out.println("invoke");});
      
  • groupingby

    groupingby提供和数据库group by相类似的操作,对流中数据进行分组。使用collect会生成Map<K, List>这种的Map对象,groupingby还可以进行二次分组,对每个已分好组的K中的List再进行分组

    Map<Boolean, List<String>> collect = Arrays.stream(stringArray).collect(Collectors.groupingBy(s -> s.length() > 5));
    System.out.println(collect);
    
    Map<Integer, List<String>> collect1 = Arrays.stream(stringArray).collect(Collectors.groupingBy(String::length));
    System.out.println(collect1);
    
    Map<Boolean, Map<Integer, List<String>>> collect2 = Arrays.stream(stringArray).collect(Collectors.groupingBy(s -> s.length() > 5, Collectors.groupingBy(String::length)));
    System.out.println(collect2);
    

    还可以使用mapping对List进行处理

    Map<Boolean, Set<String>> collect2 = Arrays.stream(stringArray).collect(Collectors.groupingBy(s -> s.length() > 5, Collectors.mapping(String::toUpperCase, Collectors.toSet())));
    System.out.println(collect2);
    

参考

  1. https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
  2. https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
  3. https://docs.oracle.com/javase/8/docs/api/index.html
  4. Java8新特性学习-Stream的Reduce及Collect方法详解
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aabond

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值