Java基础之lambda表达式

Lambda表达式

lambda 表达式

  • Lambda 表达式,也称为闭包,是推动 Java 8 发布的最重要新特性

    • Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)
    • 使用 Lambda 表达式可以使代码变的更加简洁紧凑
  • lambda 表达式的语法格式如下:

    (parameters) -> expression ;
    (parameters) ->{ statements; };
    
重要特征
  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
@FunctionalInterface  		//定义接口时添加该注解表示函数式接口

new Thread( 				//将lambda表达式作为参数传入方法,构建线程对象
    () -> { 				
        int a = 0;
        while(a < 10){
            System.out.println(++a);
        }
	}
).start();
/**
(): 函数式接口中唯一抽象方法的参数;参数类型自动匹配, 可指定,只有一个参数时可忽略()
然后跟 -> {}
方法体{ }:表示方法中的具体实现,仅一句实现语句可省略{}
*/

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)
  • Lambda 表达式主要用来定义行内执行的方法类型接口
    • 例如简单方法接口
  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力

函数式接口

  • 函数式接口(Functional Interface)是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口

    • 函数式接口可以被隐式转换为 lambda 表达式

    • Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上

    • JDK 1.8 之前已有的函数式接口:

      • java.lang.Runnable
      • java.util.concurrent.Callable
      • java.security.PrivilegedAction
      • java.util.Comparator
      • java.io.FileFilter
      • java.nio.file.PathMatcher
      • java.lang.reflect.InvocationHandler
      • java.beans.PropertyChangeListener
      • java.awt.event.ActionListener
      • javax.swing.event.ChangeListener
    • JDK 1.8 新增加的函数接口:

      • java.util.function:包含了很多类,用来支持 Java的 函数式编程
功能型函数式接口
  • 有参数接收和返回
  • Function类,例如:
@FunctionalInterface
public interface Function<T,R>{
    public R apply(T t);//接受一个参数,返回一个参数;通过方法引用实现操作
    /* 例如
    Function<String , Boolean> fun = "www.aaa" :: startsWith;
    boolean result = fun.apply("www");	//结果为true
    */
}
消费型函数式接口
  • 只能进行数据的处理,没有参数返回,例如:
@FunctionalInterface
public interface Consumer<T>{
    void accept(T t);//只进行数据处理,没有返回值
    /*
    Consumer con = System.out :: println;
    con.accept("张三");  //输出张三
    */
}
供给型函数接口
  • 无接受参数,有返回值
@FunctionalInterface
public interface Supplier<T>{
    public T get();
    /*
    Supplier<String> sup = "asd" :: toUpperCase;
    String str = sup.get();  // 将asd 转为大写
    */
}
断言型函数接口
  • 进行判断处理
@FunctionalInterface
public interface Predicate<T>{
    public Boolean test(T t);
    /*
    Predicate<String> pre = "asd" :: equals;
    boolen result = sup.test("asd");  // 结果为true
    */
}

方法引用

  • 方法引用通过方法的名字来指向一个方法
    • 方法引用可以使语言的构造更紧凑简洁,减少冗余代码
  • 方法引用使用一对冒号 ::
//静态方法:类名称 :: static方法名称;
String :: valueOf;
//引用某个实例对象的方法:instance::method
实例化对象 :: 普通方法;
//引用特定类型方法:Class::method
特定类 :: 普通方法;
//引用构造方法:Class::new,或者更一般的Class< T >::new
类名称 :: new;

Stream

Stream

意义
  • Java 8 API添加了一个新的抽象称为流Stream,以一种声明的方式处理数据。

    • 将要处理的元素集合看作一种流,
      • 流在管道中传输, 可以在管道的节点上进行处理,
      • 比如筛选, 排序,聚合等
    • 元素流在管道中经过中间操作(intermediate operation)的处理
    • 最后由最终操作(terminal operation)得到前面处理的结果
  • Stream(流):一个来自数据源的元素队列并支持聚合操作

    • 元素是特定类型的对象,形成一个队列

      • JavaStream并不会存储元素,而是按需计算。
    • 数据源:流的来源

      • 可以是集合,数组,I/O channel,产生器 generator 等
    • 聚合操作:类似SQL语句一样的操作

      • 比如 filter, map, reduce, find, match, sorted 等
  • Stream操作还有两个基础的特征

    • Pipelining:中间操作都会返回流对象本身

      • 多个操作可以串联成一个管道, 如同流式风格
        • 可以对操作进行优化, 比如延迟执行(laziness)和短路(short-circuiting)
    • 内部迭代:Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现

      • 通过Iterator或者For-Each方式显式的在集合外部进行迭代叫做外部迭代
strema执行流程
  1. 执行
+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+
  1. 转为 Java 代码
List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();
生成流

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream():为集合创建串行流。
  • parallelStream():为集合创建并行流。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

forEach

  • Stream 提供了forEach’迭代流中的每个数据

  • // 使用 forEach 输出了10个随机数
    Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
    

map

  • map 方法用于映射每个元素到对应的结果

  • List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); 
    // 获取对应的平方数 
    List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
    

filter

  • filter方法用于通过设置的条件过滤出元素

  • // 使用 filter 方法过滤出空字符串
    List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 
    // 获取空字符串的数量 
    long count = strings.stream().filter(string -> string.isEmpty()).count();
    

limit

  • limit 方法用于获取指定数量的流

  • // 用 limit 方法打印出 10 条数据
    Random random = new Random(); 
    random.ints().limit(10).forEach(System.out::println);
    

sorted

  • sorted 方法用于对流进行排序

  • Random random = new Random(); 
    // 使用 sorted 方法对输出的 10 个随机数进行排序
    random.ints().limit(10).sorted().forEach(System.out::println);
    

并行(parallel)程序

  • parallelStream 是流并行处理程序的代替方法

  • // 使用 parallelStream 来输出空字符串的数量
    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    // 获取空字符串的数量 
    long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
    // 可以很容易的在顺序运行和并行直接切换
    

Collectors

  • Collectors 类实现了很多归约操作

    • 例如将流转换成集合和聚合元素
    • Collectors 可用于返回列表或字符串:
  • List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 			
    // 获取过滤后的非空字符串集合
    List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("筛选列表: " + filtered); 
    // 获取过滤后的非空字符串集合并使用 , 拼接为字符串
    String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", ")); System.out.println("合并字符串: " + mergedString);
    

统计

  • 产生统计结果的收集器主要用于int、double、long等基本类型上

    • 可用来产生类似如下的统计结果
  • List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);  
    IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();  
    System.out.println("列表中最大的数 : " + stats.getMax()); 
    System.out.println("列表中最小的数 : " + stats.getMin()); 
    System.out.println("所有数之和 : " + stats.getSum()); 
    System.out.println("平均数 : " + stats.getAverage());
    

完整实例

public class Teste {
    public static void main(String args[]){
        System.out.println("Java 7: ");

        // 计算空字符串
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
        System.out.println("列表: " +strings);							   // 列表: [abc, , bc, efg, abcd, , jkl]
        long count = getCountEmptyStringUsingJava7(strings);   
        System.out.println("空字符数量为: " + count);							// 空字符数量为: 2
        count = getCountLength3UsingJava7(strings);
        System.out.println("字符串长度为 3 的数量为: " + count);				 // 字符串长度为 3 的数量为: 3	

        // 删除空字符串
        List<String> filtered = deleteEmptyStringsUsingJava7(strings);
        System.out.println("筛选后的列表: " + filtered);						// 筛选后的列表: [abc, bc, efg, abcd, jkl]

        // 删除空字符串,并使用逗号把它们合并起来
        String mergedString = getMergedStringUsingJava7(strings,", ");
        System.out.println("合并字符串: " + mergedString);					 // 合并字符串: abc, bc, efg, abcd, jkl
        List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

        // 获取列表元素平方数
        List<Integer> squaresList = getSquares(numbers);
        System.out.println("平方数列表: " + squaresList);					//平方数列表: [9, 4, 49, 25]
        List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);

        System.out.println("列表: " +integers);							  // 列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
        System.out.println("列表中最大的数 : " + getMax(integers));		  // 列表中最大的数 : 19
        System.out.println("列表中最小的数 : " + getMin(integers));		  // 列表中最小的数 : 1
        System.out.println("所有数之和 : " + getSum(integers));			   // 所有数之和 : 85
        System.out.println("平均数 : " + getAverage(integers));			 // 平均数 : 9
       
        System.out.print("随机数: ");
        // 输出 10 个随机数
        Random random = new Random();  
        for(int i=0; i < 10; i++){
            System.out.print(random.nextInt() + " "); 						// 随机数: -3 -963 44 -10 -88 2215 -1101 576 -104 164
        }

        System.out.println("Java 8: ");		
        System.out.println("列表: " +strings);     						 // 列表: [abc, , bc, efg, abcd, , jkl]

        count = strings.stream().filter(string->string.isEmpty()).count();
        System.out.println("空字符串数量为: " + count);					// 空字符串数量为: 2

        count = strings.stream().filter(string -> string.length() == 3).count();
        System.out.println("字符串长度为 3 的数量为: " + count);			// 字符串长度为 3 的数量为: 3

        filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList());
        System.out.println("筛选后的列表: " + filtered);					// 筛选后的列表: [abc, bc, efg, abcd, jkl]

        mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", "));
        System.out.println("合并字符串: " + mergedString);				// 合并字符串: abc, bc, efg, abcd, jkl
	
        squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList());
        System.out.println("Squares List: " + squaresList);				// Squares List: [9, 4, 49, 25]
        System.out.println("列表: " +integers);						  // 列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]

        IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();

        System.out.println("列表中最大的数 : " + stats.getMax());	      // 列表中最大的数 : 19
        System.out.println("列表中最小的数 : " + stats.getMin());		  // 列表中最小的数 : 1
        System.out.println("所有数之和 : " + stats.getSum());	 	   // 所有数之和 : 85
        System.out.println("平均数 : " + stats.getAverage());		     // 平均数 : 9.444444444444445

        System.out.println("随机数: ");  
        random.ints().limit(10).sorted().forEach(System.out::println);  // 随机数: -174 -130 -12 -779 136 555 124 12649 147 170

        // 并行处理
        count = strings.parallelStream().filter(string -> string.isEmpty()).count();
        System.out.println("空字符串的数量为: " + count);				// 空字符串的数量为: 2
    }

    // 计算空字符串 Java7
    private static int getCountEmptyStringUsingJava7(List<String> strings){
        int count = 0;
        for(String string: strings){
            if(string.isEmpty()){
                count++;
            }
        }
        return count;
    }

    // 长度为 3 的字符串数量
    private static int getCountLength3UsingJava7(List<String> strings){
        int count = 0;
        for(String string: strings){
            if(string.length() == 3){
                count++;
            }
        }
        return count;
    }

    // 删除空字符串
    private static List<String> deleteEmptyStringsUsingJava7(List<String> strings){
        List<String> filteredList = new ArrayList<String>();
        for(String string: strings){
            if(!string.isEmpty()){
                filteredList.add(string);
            }
        }
        return filteredList;
    }

    // 删除空字符串并使用 ,  拼接字符串
    private static String getMergedStringUsingJava7(List<String> strings, String separator){
        StringBuilder stringBuilder = new StringBuilder();
        for(String string: strings){
            if(!string.isEmpty()){
                stringBuilder.append(string);
                stringBuilder.append(separator);
            }
        }
        String mergedString = stringBuilder.toString();
        return mergedString.substring(0, mergedString.length()-2);
    }

    // 获取列表元素平方数
    private static List<Integer> getSquares(List<Integer> numbers){
        List<Integer> squaresList = new ArrayList<Integer>();
        for(Integer number: numbers){
            Integer square = new Integer(number.intValue() * number.intValue());
            if(!squaresList.contains(square)){
                squaresList.add(square);
            }
        }
        return squaresList;
    }

    // 获取列表中最大值
    private static int getMax(List<Integer> numbers){
        int max = numbers.get(0);
        for(int i=1;i < numbers.size();i++){
            Integer number = numbers.get(i);
            if(number.intValue() > max){
                max = number.intValue();
            }
        }
        return max;
    }

    // 获取列表中最小值
    private static int getMin(List<Integer> numbers){
        int min = numbers.get(0);
        for(int i=1;i < numbers.size();i++){
            Integer number = numbers.get(i);

            if(number.intValue() < min){
                min = number.intValue();
            }
        }
        return min;
    }

    // 列表中元素求和
    private static int getSum(List numbers){
        int sum = (int)(numbers.get(0));
        for(int i=1;i < numbers.size();i++){
            sum += (int)numbers.get(i);
        }
        return sum;
    }

    // 获取列表中元素平均数
    private static int getAverage(List<Integer> numbers){
        return getSum(numbers) / numbers.size();
    }
}