1. Lambda表达式
1.1 为什么使用Lambda表达式
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递) 。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
举例
一般书写格式:
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
lambda表达式:
Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);
1.2格式
- 基本格式:
-> :lambda操作符 or 箭头操作符
->左边:lambda形参列表(其实就是接口中抽象方法的参数列表)
->右边:lambda体(就是重写的方法的方法体,注意不用return)
- 语法格式一:无参数 无返回值
接口中抽象方法没有参数 故lambda表达式中也没有;重写方法没有返回值,故lambda表达式也没有返回值
- 语法格式二:需要一个参数 无返回值
接口中抽象方法需要一个参数,但是返回值为空;Lambda表达式中 (String t) -> 函数体
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("s = " + s);
}
};
con.accept("学习");
Consumer<String> LambdaCon = (String t) -> System.out.println("t = " + t);
LambdaCon.accept("快乐") ;
- 语法格式三: 数据类型可以省略,因为可由编译器推断出来,称为类型推断
//类型推断
List<String> list = new ArrayList<>();
int[] arr = {1,2,3};
语法格式四: Lambda表达式只需要一个参数的时候,参数的小括号可以省略
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("谎言和誓言");
Sysout1.out.println("***********************");
Consumer<String> LambdaCon = s -> System.out.println(s);
LambdaCon.accept("一个是听的人当真了,一个是说的人当真了");
语法格式五:Lambda表达式需要两个及以上参数,多条执行语句,并且可以拥有返回值。
当lambda表达式参数不止一个的时候,必须使用小括号(),因为类型推断机制,可以不声明参数的类型,多个参数使用逗号分隔。当lambda体有多条执行语句的时候必须加上{}
。当方法体只有一条rerun语句的时候,lambda表达式中可以省略return;当有多条语句的时候,返回值需要添加return。
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1 = " + o1);
System.out.println("o2 = " + o2);
return Integer.compare(o1,o2);
}
};
com.compare(12,20);
System.out.println("******************************");
Comparator<Integer> lambdaCon = (o1, o2) -> {
System.out.println("o1 = " + o1);
System.out.println("o2 = " + o2);
return Integer.compare(o1,o2);
};
lambdaCon.compare(25, 15);
Comparator<Integer> lambdaCon2 = (o1, o2) -> Integer.compare(o1,o2);
System.out.println(lambdaCon2.compare(30,03));
1.3Lambda表达式的本质
java—万事万物皆对象
lambda表达式在java中依然作为对象 —-接口的实例对象Comparator<Integer> lambdaCon2 = (o1, o2) -> Integer.compare(o1,o2);声明对象
**
2. 函数式接口
如果一个接口中,只声明了一个抽象方法,则此接口称为函数式接口
●你可以通过Lambda表达式来创建该接口的对象。( 若Lambda表达式抛出一一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明),
●我们可以在一个接口, 上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。同javadoc也会包含一条声明,说明这个接口是一个函数式接口。
●在java.util.function包下定 义了Java 8的丰富的函数式接口
2.1如何理解函数式接口
● Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程就是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)。
● 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的
对象类型——函数式接口。
● 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
● 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
2.2java内置的函数式接口
/**
* 消费型接口 Consumer<T> void accept(T t)
* 供给型接口 Supplier<T> T get()
* 函数型接口 Function<T,R> R apply(T t)
* 断定型接口 boolean test(T t)
*
* 核心函数式接口 消费型
*/
@Test
public void test4(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("学习累了,去消费了"+aDouble+"元!");
}
});
System.out.println("************************************");
happyTime(100, money -> System.out.println("搬砖辛苦赚了"+money+"元!"));
}
public void happyTime(double money,Consumer<Double> con) {
con.accept(money);
}
/**
* 核心函数式接口 断言型
*/
@Test
public void test5(){
List<String> stringList = Arrays.asList("北京","南京","天津","西京","东京");
List<String> strings = filterStr(stringList, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
strings.stream().forEach(item -> System.out.println(item));
System.out.println("***************************************");
List<String> strings2 = filterStr(stringList, s -> s.contains("京"));
strings2.stream().forEach(item -> System.out.println(item));
}
public List<String> filterStr(List<String> list, Predicate<String> predicate) {
List<String> filter = new ArrayList<>();
list.stream().forEach(str -> {
if(predicate.test(str)) {
filter.add(str);
}
});
return filter;
}
以上接口都是核心函数式接口的变形。可以灵活应用lambda表达式。
3. 方法引用
lambda表达式作为函数式接口的抽象方法的一个实例,简化操作。
将new 的形式创建实例对象并重写抽象方法 转变成 lambda表达式
/**
* 情况一:对象::实例方法
* Consumer中void accept(T t)方法
* PrintStream中的void println(T t)方法
*/
@Test
public void method1() {
//lambda
Consumer<String> con1 = s -> System.out.println(s);
con1.accept("北京");
System.out.println("********************************");
//方法引用
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("beijing");
}
/**
* Supplier<T> 中T get()方法
* Employee 中的 String getName()方法
*/
@Test
public void method2() {
Employee emp = new Employee(1010,"Tom",23,5600.5);
Supplier<String> supplier = () -> emp.getName();
System.out.println(supplier.get());
System.out.println("********************************");
Supplier<String> supplier1 = emp::getName;
System.out.println(supplier1.get());
}
/**
* 情况二:类::静态方法
* Comparator中int compare(T t1,T t2)方法
* Integer 中的int compare(T t,U u)方法
*/
@Test
public void method3() {
//lambda
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(20, 02));
}
/**
* Function中的R apply(T t)
* Math中的Long round(Double d)
*/
@Test
public void method4() {
Function<Double,Long> func1 = (d) -> Math.round(d);
Long apply = func1.apply(3.14);
System.out.println("apply = " + apply);
System.out.println("********************************");
Function<Double,Long> func2 = Math::round;
System.out.println("apply = " + func2.apply(3.66));
}
/**
* 情况3:类::实例方法(有难度)
* Comparator中的int compare(T t1,T t2)
* String中的 int t1.compareTo(t2)
*/
@Test
public void method5() {
Comparator<String> comparator1 = (t1,t2) -> t1.compareTo(t2);
int compare = comparator1.compare("", "123");
System.out.println("compare = " + compare);
System.out.println("***********************************");
Comparator<String> comparator2 = String::compareTo;
int compare1 = comparator2.compare("123", "");
System.out.println("compare1 = " + compare1);
}
/**
* BiPredicate<T,U></>中的boolean test(T t,U u)
* String 中的boolean t.equals(u)
*/
@Test
public void method6() {
BiPredicate<String,String> predicate = (t1,t2) -> t1.equals(t2);
System.out.println(predicate.test("asd", "asd"));
System.out.println("***********************************");
BiPredicate<String,String> predicate2 = String::equals;
System.out.println(predicate2.test("asd", "jkl"));
}
/**
* Function<T,R> 中的 R apply(T t)
* Employee 中的String getName()
*/
@Test
public void method7() {
Employee employee = new Employee(1004, "123", 18, 2500);
Function<Employee, String> func = t -> t.getName();
String apply = func.apply(employee);
System.out.println("apply = " + apply);
System.out.println("***********************************");
Function<Employee,String> fun2 = Employee::getName;
System.out.println("apply = " + fun2.apply(employee));
}
4.Stream api
4.1Stream
Stream到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,Stream讲的是计算”
注意:
- Stream不会自己存储元素
- Stream不会改变源对象。相反他们会返回一个持有结果的新Stream
- Stream是延迟操作的,意味着这些操作会等到需要结果的时候才执行
4.2 Stream操作流程
_1.
Stream关注的是对数据的运算,与CPU打交道
集合关注的是数据的存储,与内存打交道
2.
Stream不会自己存储元素
Stream不会改变源对象。相反他们会返回一个持有结果的新Stream
Stream是延迟操作的,意味着这些操作会等到需要结果的时候才执行 3.Sream执行流程
stream实例化 中间操作(过滤,映射,排序) 终止操作 4.说明
(1) 一个中间操作链,对数据源的数据进行处理
(2) 一旦执行中止操作,就执行中间操作链,并产生结果。之后,不好再被使用
1. 创建
//创建Stream 方式一 通过集合Collection的拓展流
// default Stream<E> stream() 返回一个顺序流 -> 从数据源(集合)中获取数据的时候按照原有顺序逐个获取
// default Stream<E> parallelStream() 返回一个并行流 -> 从数据源(集合)中随机获取数据的时候
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
//stream 的串行流
Stream<Employee> stream = employees.stream();
//stream的并行流
Stream<Employee> employeeStream = employees.parallelStream();
}
//创建Stream 方式二 通过数组工具类 Arrays
// public static <T> Stream<T> stream(T[] array)
// intStream stream(int[] array)
// LongStream stream(long[] array)
// DoubleStream stream(double[] array)
@Test
public void test2() {
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr); //放入int型数组,自动创建IntStream
List<Employee> employees = EmployeeData.getEmployees();
Employee[] arr2 = new Employee[]{employees.get(1),employees.get(2)};
Stream<Employee> stream1 = Arrays.stream(arr2); //放入自定义类型数组 创建一般的流
}
/**
* 创建的方式三 通过Stream 的 of()
* public static<T> Stream<T> of(T t)
* public static<T> Stream<T> of(T... values)
*/
@Test
public void test3() {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<String> integerStream2 = Stream.of("a");
}
/**
* 创建的方式四 创建无限流
* 迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
* seed 迭代的种子(初始值),f迭代的函数 返回类型和种子的类型一致,对结果进行迭代
* 生成 public static<T> Stream<T> generate(Supplier<T> s)
* s 用于生成的函数
*/
@Test
public void test4() {
Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
2.中间操作
3.终止操作
5.Option类
目前为止 臭名昭著的空指针异常是导致java程序异常的主要原因。为了解决空指针异常,Google公司引入了Guava中的Optional类,通过检查使用空值的方式来防止代码污染。
Optional
doc中描述:这是一个可以为null的容器对象,如果值存在,这isPresent()方法会返回true,调用get()方法返回该对象
- 创建Optional类对象的方法
```java
public static
Optional of(T value) 创建一个Optional实例 value必须非空
public static
public static
- 判断optional容器中对象是否包含对象
```java
//判断是否包含对象
boolean isPresent()
//如果有值就执行Consumer接口的实现代码,并且该值会则为参数传递给它
void ifPresent(Consumer<? super T> consumer)
- 获取optional容器中的对象:
//如果调用对象包含值 返回该值 否则抛出异常
public T get()
//如果有值 则将其返回,否则返回指定的other对象
public T orElse(T other)
//如果有值 则将其返回,否则返回由Supplier接口实现提供的对象
public T orElseGet(Supplier<? extends T> other)
//如果有值 则将其返回,否则返回由Supplier接口实现提供的异常
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)