Lambda表达式是一个匿名方法,可以将行为像数据一样传递
lanbda是一个匿名函数,是一段可以传递的代码,使用Lambda需要函数式接口的支持
引入新操作符: -> 箭头操作符/Lambda操作符
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需执行功能,即Lambda体
1)语法1:无参,无返回值
() -> System.out.println("...");
2)语法2:有一个参数,并且无返回值(若只有一个参数,小括号可以省略不写)
(x) -> System.out.println(x);
3)语法3:有多个参数,有返回值,有多条语句
如果有多条语句,Lambda体得使用大括号包起来
Comparator con = (x,y) -> {
System.out.println(x);
return Integer.compare(x,y);
}
4)语法4:若Lambda只有一条语句,大括号和return都可以省略不写
5)语法5:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM可以通过上下文推断出数据类型,即类型推断
例子:
/**
* Lambda表达式的多种写法
*/
@Test
public void test03() {
//写法1.无参,单句程序,无返回值 :() -> 程序
Runnable runn = () -> System.out.println("写法1");
runn.run();
System.out.println("----------------------------");
//写法2,无参,多条语句,无返回值,函数体内由多条语句时需要用大括号括起来
Runnable runn2 = () -> {
System.out.print("写法2:");
System.out.println("多条语句");
};
runn2.run();
System.out.println("----------------------------");
//写法3,有参,多条语句,无返回值
Consumer con = (x) -> {
int sum = x + x;
System.out.println("写法3:" + sum);
};
con.accept(6);
System.out.println("----------------------------");
//写法4:单参,多条语句,有返回值
Function fun = (x) -> {
int sum = x + x;
System.out.println("写法4:" + sum * sum);
return sum * sum;
};
fun.apply(6);
System.out.println("----------------------------");
//写法5:多参,多条语句,有返回值
Comparator con1 = (x,y) -> {
System.out.println("写法5:" + x + ":" + y);
return x + y;
};
int sum = con1.compare(1, 2);
System.out.println(sum);
}
结果:
写法1
----------------------------
写法2:多条语句
----------------------------
写法3:12
----------------------------
写法4:144
----------------------------
写法5:1:2
3
在书写Lambda表达式时,我们可以省略Lambda表达式中的所有参数类型。
原因是:Java编译器会根据上下文信息推断出所有的参数类型。
例如:
Comparator<Integer> con1 = (x,y) -> {
System.out.println("写法5:" + x + ":" + y);
return x + y;
};
--------------------------------------------------------------------
List<Integer> list = new ArrayList<>();//后面未指定泛型,由编译器根据上下文进行类型推断
可以通过 Lambda 表达式来创建该函数式接口的对象。(若 Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。 可以在任意函数式接口上使用 @FunctionalInterface 注解,用于检查它是否是一个函数式接口。
例子1:
函数式接口:MyFun
@FunctionalInterface //使用注解标注函数式接口
public interface MyFun {
T handler(T t);//函数式接口只有一个抽象方法
}
测试
/**
* 函数式接口
* 计算平方
*/
@Test
public void test06() {
MyFun mf = x -> x * x;
int num = mf.handler(2);
System.out.println(num);
}
结果:
4
例子2:
//作为参数传递
public String toUpper1(String s,MyFun mf) {
return mf.handler(s);
}
/**
* 将字符串转变为大写
*/
@Test
public void test02() {
System.out.println(toUpper1("abcdefg", (str) -> str.toUpperCase()));
}
结果:
ABCDEFG
四大核心接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get(); |
Function 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t); |
Predicate 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t); |
其他接口
1.BiFunction<T,U,R> 参数类型:T, U 返回值类型:R 对类型为 T, U 参数应用操作,返回 R 类型的结果。包含方法为 R apply(T t, U u);
2.UnaryOperator<T> 参数类型:T 返回值类型:T 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(T t);
(Function 子接口)
3.BinaryOperator<T> 参数类型:T,T 返回值类型:T 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为 T apply(T t1, T t2);
(BiFunction子接口)
4.BiConsumer<T,U> 参数类型:T, U 返回值类型:void 对类型为T, U 参数应用操作。包含方法为void accept(T t, U u)
若Lambda体中的内容有方法已经实现了,我们可以使用方法引用(方法引用式Lambda表达式的另一种表现形式)
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)
方法引用:使用操作符 :: 将方法名和对象或类的名字分隔开来
主要有三种语法格式
(注意Lambda体中调用方法的参数列表与返回值类型要与函数式接口抽象方法中的参数列表与返回值类型保持一致)
1)对象::实例方法名
2)类::静态方法名
3)类::实例方法名
若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用 类名::method
格式:类名::new
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
格式:Type::new
例子:
public class TestMethod_05 {
public static int say() {
System.out.println("Hello world");
return 0;
}
public void speek(String s) {
System.out.println("Hello world " + s);
}
//方法引用
@Test
public void test01() {
//引用静态方法 类名::方法名
//打印数字
//原本使用
Consumer s = (x) -> System.out.println(x);
s.accept(355);
//使用方法引用
System.out.println("-------------------");
Consumer s0 = System.out::println;
s0.accept(355);
System.out.println("-------------------");
//调用say方法
Supplier s1 = TestMethod_05::say;//引用静态方法,类名::方法名
s1.get();
System.out.println("-------------------");
//将数字排序
Stream.of(1,5,9,3,2,7,5,6,0)
.sorted(Integer::compare)//引用Integer的 public static int compare(int x, int y)方法
.forEach(System.out::println);
//引用实例方法 对象::方法名
System.out.println("-------------------");
TestMethod_05 t = new TestMethod_05();
Consumer con = t::speek;//对象::方法名
con.accept("fjut");
//构造器引用
System.out.println("-------------------");
//原本操作
Function fun2 = (x) -> new Person(x,0);
Person p = fun2.apply("zhangsan");
System.out.println(p);
//构造器引用
Function fun3 = Person::new;//使用构造器引用的前提是,原始类内部由与之对应的构造方法
Person p2 = fun3.apply("lisi");
System.out.println(p2);
//数组引用
//创建数组
//原本操作
System.out.println("-------------------");
Function fun = (n) -> new Integer[n];
System.out.println(fun.apply(8).length);
//数组引用
System.out.println("-------------------");
Function fun4 = Integer[]::new;
System.out.println(fun4.apply(8).length);
}
}
结果:
355
-------------------
355
-------------------
Hello world
-------------------
0
1
2
3
5
5
6
7
9
-------------------
Hello world fjut
-------------------
Person [name=zhangsan, age=0]
Person [name=lisi, age=0]
-------------------
8
-------------------
8
Stream 提供了一种高效且易于使用的处理数据的方式,可以使用链式操作对于数据进行处理
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 是惰性求值,他们会等到需要结果的时候才执行
判断是惰性求值还是及早求值只需要看他的返回值,若返回值为Stream则为惰性求值,如果返回值是一个值或者是空,则为及早求值,惰性求值的设计是为了形成一个筛选链,可以对Stream进行多重操作。
一个数据源(如:集合、数组),获取一个流
a.由集合创建流
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法 :
default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流
例子:
@Test
public void test07() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Stream s = list.stream();
Stream s1 = list.parallelStream();//并行流
//迭代操作
s.forEach(System.out::println);
s1.forEach(System.out::println);
}
结果:
1
2
3
2
3
1
b.由数组创建流
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static <T> Stream<T> stream(T[] array): 返回一个流
例子:
@Test
public void test08() {
int[] nums = new int[] {1,2,3,4,5,6};
IntStream s = Arrays.stream(nums);
//迭代
s.forEach(System.out::println);
}
结果:
1
2
3
4
5
6
c.重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
d.由值创建流
可以使用静态方法 Stream.of(), 通过显示值
创建一个流。它可以接收任意数量的参数。
public static Stream of(T... values) : 返回一个流
例子:
@Test
public void test09() {
Stream s = Stream.of(1,2,3,4,5,6);
s.forEach(System.out::println);
}
结果:
1
2
3
4
5
6
e.由函数创建流:创建无限流
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
迭代
public static Stream iterate(final T seed, final UnaryOperator f)
生成
public static Stream generate(Supplier s)
例子:
@Test
public void test10() {
Stream s = Stream.iterate(1, (x) -> x * 2); //迭代操作,每一个数为前一个数的两倍,从种子位置开始,也就是从1开始计算
s.limit(10).forEach(System.out::println);//截取前10个并打印
System.out.println("---------------------------------------------------");
Stream s1 = Stream.generate(() -> Math.random());//生成操作,生成随机数
s1.limit(10).forEach(System.out::println);//截取前10个并打印
}
结果:
1
2
4
8
16
32
64
128
256
512
---------------------------------------------------
0.7621967170904101
0.9663230676320194
0.6513602778336912
0.9526431093691002
0.10407207262053253
0.7543354059250785
0.1259084660829647
0.49821106170579876
0.10789018662385141
0.7834201018302808
一个中间操作链,对数据源的数据进行处理
filter(Predicate super T> predicate) 遍历数据并检查其中元素,返回由与此给定谓词匹配的此流的元素组成的流
distinct() 筛选去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
例子:
Person类:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
测试:
//筛选与切片
// filter(Predicate super T> predicate) 遍历数据并检查其中元素,返回由与此给定谓词匹配的此流的元素组成的流
// distinct() 筛选去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
// limit(long maxSize) 截断流,使其元素不超过给定数量
// skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
@Test
public void test01() {
//添加元素
List list = new ArrayList<>();
list.add(new Person("zhangsan", 15));
list.add(new Person("lisi", 10));
list.add(new Person("lisi", 10));
list.add(new Person("lisi", 10));
list.add(new Person("wangwu", 16));
list.add(new Person("zhaoliu", 18));
//筛选年龄大于15的人
list.stream()
.filter((person) -> person.getAge() > 15)
.forEach(System.out::println);
System.out.println("------------------------------");
//去除列表中重复的人
list.stream()
.distinct()
.forEach(System.out::println);
System.out.println("------------------------------");
//截取前3条数据
list.stream()
.limit(3)
.forEach(System.out::println);
System.out.println("------------------------------");
//跳过前3条数据
list.stream()
.skip(3)
.forEach(System.out::println);
}
结果:
Person [name=wangwu, age=16]
Person [name=zhaoliu, age=18]
------------------------------
Person [name=zhangsan, age=15]
Person [name=lisi, age=10]
Person [name=wangwu, age=16]
Person [name=zhaoliu, age=18]
------------------------------
Person [name=zhangsan, age=15]
Person [name=lisi, age=10]
Person [name=lisi, age=10]
------------------------------
Person [name=lisi, age=10]
Person [name=wangwu, age=16]
Person [name=zhaoliu, age=18]
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
例子:
// 映射
// map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
// mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
// mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
// mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。。
// flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
@Test
public void test02() {
List<Person> list = new ArrayList<>();
list.add(new Person("zhangsan", 15));
list.add(new Person("lisi", 10));
list.add(new Person("lisi", 10));
list.add(new Person("lisi", 10));
list.add(new Person("wangwu", 16));
list.add(new Person("zhaoliu", 18));
List<Person> list2 = new ArrayList<>();
list2.add(new Person("zhangsan2", 15));
list2.add(new Person("lisi2", 10));
list2.add(new Person("lisi2", 10));
list2.add(new Person("lisi2", 10));
list2.add(new Person("wangwu2", 16));
list2.add(new Person("zhaoliu2", 18));
//获取所有员工姓名
list.stream()
.map((person) -> person.getName())
.distinct()
.forEach(System.out::println);
System.out.println("-----------------------------");
//获取所有员工信息
//flatMap用于将多个流 Stream转化为一个流
Stream.of(list,list2)
.flatMap((l) -> l.stream().map((person) -> person.getName())) //获取姓名
.forEach(System.out::println);
}
结果:
zhangsan
lisi
wangwu
zhaoliu
-----------------------------
zhangsan
lisi
lisi
lisi
wangwu
zhaoliu
zhangsan2
lisi2
lisi2
lisi2
wangwu2
zhaoliu2
sorted() 产生一个新流,其中按自然顺序排序
sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
例子:
// sorted() 产生一个新流,其中按自然顺序排序
// sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
@Test
public void test03() {
//数字排序
Stream.of(5,6,3,8,9,2,1)
.sorted()
.forEach(System.out::println);
System.out.println("---------------------");
//将数字从小到大排序
Stream.of(5,6,3,8,9,2,1)
.sorted((x,y) -> -Integer.compare(x, y))
.forEach(System.out::println);
System.out.println("---------------------");
//先按年龄排序,若年龄相同再按年龄相同按名字比较
List<Person> list = new ArrayList<>();
list.add(new Person("zhangsan", 15));
list.add(new Person("lisi", 10));
list.add(new Person("lisi3", 10));
list.add(new Person("lisi2", 10));
list.add(new Person("wangwu", 16));
list.add(new Person("zhaoliu", 18));
list.stream()
.sorted((p1,p2) -> {
if(p1.getAge() == p2.getAge()) {
return p1.getName().compareTo(p2.getName());
}else {
return Integer.compare(p1.getAge(), p2.getAge());
}
})
.forEach(System.out::println);
}
结果:
1
2
3
5
6
8
9
---------------------
9
8
6
5
3
2
1
---------------------
Person [name=lisi, age=10]
Person [name=lisi2, age=10]
Person [name=lisi3, age=10]
Person [name=zhangsan, age=15]
Person [name=wangwu, age=16]
Person [name=zhaoliu, age=18]
一个终止操作,执行中间操作链,并产生结果
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void,为及早求值
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
例子:
//查找与匹配
// allMatch(Predicate p) 检查是否匹配所有元素
// anyMatch(Predicate p) 检查是否至少匹配一个元素
// noneMatch(Predicate p) 检查是否没有匹配所有元素
// findFirst() 返回第一个元素
// findAny() 返回当前流中的任意元素
// count() 返回流中元素总数
// max(Comparator c) 返回流中最大值
// min(Comparator c) 返回流中最小值
// reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
@Test
public void test04() {
List list = new ArrayList<>();
list.add(new Person("zhangsan", 15));
list.add(new Person("lisi", 10));
list.add(new Person("lisi3", 10));
list.add(new Person("lisi2", 10));
list.add(new Person("wangwu", 16));
list.add(new Person("zhaoliu", 18));
//判断所有人的年龄是否都是10
boolean b1 = list.stream()
.allMatch((p) -> p.getAge() == 10);
System.out.println(b1);
System.out.println("----------------------------");
//判断是否存在年龄为10的人
boolean b2 = list.stream()
.anyMatch((p) -> p.getAge() == 10);
System.out.println(b2);
System.out.println("----------------------------");
//返回第一个人信息
Optional<Person> op = list.stream()
.findFirst();
System.out.println(op.get());
System.out.println("----------------------------");
//返回当前总人数
Long nums = list.stream()
.count();
System.out.println(nums);
System.out.println("----------------------------");
//返回年龄最大的人
Optional<Person> op2 = list.stream()
.max((x,y) -> Integer.compare(x.getAge(), y.getAge()));
System.out.println(op2.get());
System.out.println("----------------------------");
//返回年龄最小的人
Optional<Person> op3 = list.stream()
.min((x,y) -> Integer.compare(x.getAge(), y.getAge()));
System.out.println(op3.get());
}
结果:
false
----------------------------
true
----------------------------
Person [name=zhangsan, age=15]
----------------------------
6
----------------------------
Person [name=zhaoliu, age=18]
----------------------------
Person [name=lisi, age=10]
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
forEach(Consumer c) 内部迭代(使用Collection接口需要用户去做迭代,称为 外部迭代 。相反,Stream API 使用内部迭代 —— 它帮你把迭代做了) )
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
例子:
//归约
//reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
@Test
public void test05() {
//计算求和
int sum = Stream.of(1,2,3,4)
.reduce(0, (x,y)-> x+y);
System.out.println(sum);
System.out.println("----------------------------");
List<Person> list = new ArrayList<>();
list.add(new Person("zhangsan", 15));
list.add(new Person("lisi", 10));
list.add(new Person("lisi3", 10));
list.add(new Person("lisi2", 10));
list.add(new Person("wangwu", 16));
list.add(new Person("zhaoliu", 18));
//拼接名字并转化为大写
String names = list.stream()
.map((p)->p.getName()) //获取姓名映射
.reduce("",(p1,p2) -> p1.concat(p2).toUpperCase());//拼接姓名,并转化为大写
System.out.println(names);
}
结果:
10
----------------------------
ZHANGSANLISILISI3LISI2WANGWUZHAOLIU
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
例子:
//收集
//collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
@Test
public void test06() {
//转化为List集合
List<Integer> list = Stream.of(1,2,3,4,5,6)
.collect(Collectors.toList());
list.forEach(System.out::println);
}
结果:
1
2
3
4
5
6
Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例
Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default 关键字修饰。接口中允许添加静态方法。
public interface TestInterFace {
default void say() { //默认方法
System.out.println("say");
}
static void speek() { //静态方法
System.out.println("speek");
}
}
实现类:
public class TestInterFaceImpl implements TestInterFace{
}
测试:
@Test
public void test03() {
TestInterFace.speek();
TestInterFace t = new TestInterFaceImpl();
t.say();
}
结果:
speek
say
1)
LocalDate:表示日期
LocalTime:表示时间
LocalDateTime:表示日期时间
2) Instant:时间戳,1970年到某个时间的毫秒值
Instant.now();//默认获取UTC时间
3) Duration:计算两个时间之间的间隔
Period:计算两个日期的间隔
4)时间校正器
TemporalAdjuster:时间校正器
5)时间格式化与时区处理
DateTimeFormatter:格式化时间日期
位于java.time.format.DateTimeFormatter包下
时区:ZonedDate,ZonedTime
Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
Optional.of(T t) : 创建一个 Optional 实例
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。