Java8新特性之方法引用&Stream流

Java8新特性

方法引用

前言

什么是函数式接口

  • 只包含一个抽象方法的接口,称为函数式接口

  • 可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。

  • 可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

如何理解函数式接口

  • 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
  • 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
  • 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

方法引用

方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。

要求:实现接口的抽象方法的参数列表和返回值类型,必须与引用的方法的参数列表和返回值类型保持一致!

格式:使用操作符 :: 将类(或对象) 与 方法名分隔开来。

如下三种主要使用情况:

  • 对象实例 :: 方法名
  • 类 :: 静态方法名
  • 类 :: 实例方法名

例如x -> System.out.println(x)等价于 System.out::println

举例

  List<Integer> list = Arrays.asList(82,22,34,50,9);
        //原始写法
        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(list);
        
        //lambda方式
        list.sort((o1,o2) -> o2 - o1);
        System.out.println(list);
        
        //方法引用
        list.sort(Integer::compareTo);
        System.out.println(list);
/*
输出结果
[82, 50, 34, 22, 9]
[82, 50, 34, 22, 9]
[9, 22, 34, 50, 82]
    */
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
	Consumer<String> con1 = str -> System.out.println(str);
	con1.accept("北京");

	System.out.println("*******************");
	PrintStream ps = System.out;
	Consumer<String> con2 = ps::println;
	con2.accept("beijing");
}

//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
	Employee emp = new Employee(1001,"Tom",23,5600);

	Supplier<String> sup1 = () -> emp.getName();
	System.out.println(sup1.get());

	System.out.println("*******************");
	Supplier<String> sup2 = emp::getName;
	System.out.println(sup2.get());

}

// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
	Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
	System.out.println(com1.compare(12,21));

	System.out.println("*******************");

	Comparator<Integer> com2 = Integer::compare;
	System.out.println(com2.compare(12,3));

}

//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
	Function<Double,Long> func = new Function<Double, Long>() {
		@Override
		public Long apply(Double d) {
			return Math.round(d);
		}
	};

	System.out.println("*******************");

	Function<Double,Long> func1 = d -> Math.round(d);
	System.out.println(func1.apply(12.3));

	System.out.println("*******************");

	Function<Double,Long> func2 = Math::round;
	System.out.println(func2.apply(12.6));
}

// 情况:类 :: 实例方法  (有难度)
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
	Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
	System.out.println(com1.compare("abc","abd"));

	System.out.println("*******************");

	Comparator<String> com2 = String :: compareTo;
	System.out.println(com2.compare("abd","abm"));
}

//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
	BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
	System.out.println(pre1.test("abc","abc"));

	System.out.println("*******************");
	BiPredicate<String,String> pre2 = String :: equals;
	System.out.println(pre2.test("abc","abd"));
}

// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
	Employee employee = new Employee(1001, "Jerry", 23, 6000);


	Function<Employee,String> func1 = e -> e.getName();
	System.out.println(func1.apply(employee));

	System.out.println("*******************");

	Function<Employee,String> func2 = Employee::getName;
	System.out.println(func2.apply(employee));


}

构造器引用

格式: 类名 :: new

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象

数组引用

**格式: ** type[] :: new

Function<Integer,String[]> func1 = (length) -> new String[length];
String[] arr1 = func1.apply(5);
//等价于
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);

可参考文档

传送门:廖雪峰

Stream API

为什么要使用Stream API

实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理

Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。

什么是 Stream

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

集合讲的是数据,Stream讲的是计算!

注意:

  • Stream 自己不会存储元素。

  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream操作的三个步骤

  1. 创建Stream
  2. 中间操作
  3. 终止操作

创建Stream

  • Java8 中的 Collection 接口被扩展,提供了两个获取流的方法

    • default Stream<E> stream() : 返回一个顺序流
    • default Stream<E> parallelStream() : 返回一个并行流
  • Java8 中的 Arrays 的静态方法 stream() 可以获取数组流

    • static <T> Stream<T> stream(T[] array)😗* 返回一个流
  • 通过Stream的of()

Stream 的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为惰性求值

1-筛选与切片

2-映射

3-排序

终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

流进行了终止操作后,不能再次使用。

  • count() 返回流中元素总数
  • max(Comparator c)返回流中最大值
  • min(Comparator c) 返回流中最小值
  • forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)

归约和收集

  • reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
  • collect(Collector c)将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

Collectors

实战

map和flatMap

flatMap 方法的作用:

flat 是平铺的意思,flatMap 即对流中每个元素进行平铺后,形成多个流合在一起

假如我们有这样一个需求给定单词列表[“Hello”,”World”],你想要返回列表[“H”,”e”,”l”, “o”,”W”,”r”,”d”],如果我们使用java实现这个需求,你会怎么实现呢?可能我们第一个想法是下面这种写法:

String[] strings = {"Hello", "World"};
List<String[]> collect = Stream.of(strings)
    .map(word -> word.split(""))
    .distinct()
    .collect(Collectors.toList());

System.out.println(collect.toString());

结果输出:

[[Ljava.lang.String;@7ef20235, [Ljava.lang.String;@27d6c5e0]

结果是 2 个数组元素。

再来看使用 flatMap 方法的效果:

String[] strings = {"Hello", "World"};
List<String> collect = Stream.of(strings)
    .map(s -> s.split(""))
    .flatMap(s -> Stream.of(s))
    .distinct()
    .collect(Collectors.toList());
System.out.println(collect.toString());

结果输出:

[H, e, l, o, W, r, d]

传送门

posted @ 2021-05-15 18:07  至安  阅读(653)  评论(0编辑  收藏  举报