Lambda表达式
Lambda表达式介绍
Lambda是一个匿名函数,可以理解为一段可以传递的代码。
简化匿名内部类的使用,语法更加简单。
Lambda的标准格式
Lambda省去面向对象的条条框框,Lambda的标准格式格式由3个部分组成:
(参数类型 参数名称) -> { 代码体; }
格式说明:
(参数类型 参数名称):参数列表
{代码体;}:方法体
-> :箭头,分隔参数列表和方法体
Lambda省略格式
在Lambda标准格式的基础上,使用省略写法的规则为:
小括号内参数的类型可以省略
如果小括号内有且仅有一个参数,则小括号可以省略
如果大括号内有且仅有一个语句,可以同时省略大括号、return关键字及语句分号
例如:
(int a) ->{
return new Person();
}
省略后
a -> new Person()
接口新增两个方法
JDK8以前的接口:
interface 接口名 {
静态常量;
抽象方法;
}
JDK 8对接口的增强,接口还可以有默认方法和静态方法
JDK 8的接口:
interface 接口名 {
静态常量;
抽象方法;
默认方法;
静态方法;
}
接口引入默认方法
接口中的默认方法实现类不必重写,可以直接使用,实现类也可以根据需要重写。这样就方便接口的扩展。
接口默认方法的定义格式
interface 接口名 {
修饰符 default 返回值类型 方法名() {
代码;
}
}
接口静态方法
为了方便接口扩展,JDK 8为接口新增了静态方法。
接口静态方法的定义格式
interface 接口名 {
修饰符 static 返回值类型 方法名() {
代码;
}
}
接口静态方法的使用
直接使用接口名调用即可:接口名.静态方法名();
常用内置函数式接口
它们主要在 java.util.function 包中。下面是最常用的几个接口。
- Supplier接口
- Consumer接口
- Function接口
- Predicate接口
Supplier接口
java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类
型的对象数据。
@FunctionalInterface
public interface Supplier<T> {
public abstract T get();
}
供给型接口,通过Supplier接口中的get方法可以得到一个值,无参有返回的接口。
Consumer接口
java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛
型参数决定。
@FunctionalInterface
public interface Consumer<T> {
public abstract void accept(T t);
}
要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。
Function接口
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,
后者称为后置条件。有参数有返回值。
@FunctionalInterface
public interface Function<T, R> {
public abstract R apply(T t);
}
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。
Predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用
java.util.function.Predicate 接口。
@FunctionalInterface
public interface Predicate<T> {
public abstract boolean test(T t);
}//Predicate接口用于做判断,返回boolean类型的值
方法引用
方法引用格式
符号表示 ::
符号说明 : 双冒号为方法引用运算符,而它所在的表达式被称为方法引用。
应用场景 : 如果Lambda所要实现的方案 , 已经有其他方法存在相同方案,那么则可以使用方法引用。
常见引用方式
方法引用在JDK 8中使用方式相当灵活,有以下几种形式:
instanceName::methodName 对象::方法名
ClassName::staticMethodName 类名::静态方法
ClassName::methodName 类名::普通方法
ClassName::new 类名::new (调用的构造器)
TypeName[]::new String[]::new(调用数组的构造器)
对象名::引用成员方法
// 对象::实例方法
Supplier<Long> supp2 = now::getTime;
System.out.println(supp2.get());
类名::引用静态方法
// 类名::静态方法
Supplier<Long> supp2 = System::currentTimeMillis;
System.out.println(supp2.get());
类名::引用实例方法
// 类名::实例方法
Function<String, Integer> f2 = String::length;
System.out.println(f2.apply("abc"));
类名::new 引入构造器
// 类名::new
BiFunction<String, Integer, Person> fun2 = Person::new;
System.out.println(fun2.apply("张三", 18));
数组::new 引用数组构造器
// 类型[]::new
Function<Integer, String[]> fun2 = String[]::new;
String[] arr2 = fun.apply(5);
System.out.println(arr2 + ", " + arr2.length);
Stream流
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工
处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
获取Stream流的两种方式
方式一:根据Collection获取
首先, java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流。
// 集合获取流
// Collection接口中的方法: default Stream<E> stream() 获取流
List<String> list = new ArrayList<>() ;
Stream<String> stream1 = list.stream() ;
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream() ;
Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream() ;
java.util.Map 接口不是 Collection 的子接口,所以获取对应的流需要分key、value或entry等情况:
// Map获取流
Map<String, String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
方式二:Stream中的静态方法of获取流
由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单:
// Stream中的静态方法: static Stream of(T... values)
Stream<String> stream6=Stream.of("aa","bb","cc");
String[]arr={"aa","bb","cc"};
Stream<String> stream7=Stream.of(arr);
Integer[]arr2={11,22,33};
Stream<Integer> stream8=Stream.of(arr2);
// 注意:基本数据类型的数组不行
int[]arr3={11,22,33};
Stream<int[]>stream9=Stream.of(arr3);
Stream注意事项
- 终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和
forEach 方法。
- 非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结方法。)
注意事项
Stream只能操作一次
Stream方法返回的是新的流
Stream不调用终结方法,中间的操作不会执行
Stream常用方法
forEach方法
forEach 用来遍历流中的数据
void forEach(Consumer<? super T> action);
count方法
Stream流提供 count 方法来统计其中的元素个数:
filter方法
fifilter用于过滤数据,返回符合过滤条件的数据
方法声明:
Stream<T> filter(Predicate<? super T> predicate);
limit方法
limit 方法可以对流进行截取,只取用前n个。方法声明:
Stream<T> limit(long maxSize);
skip方法
如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:
Stream<T> skip(long n);
map方法
如果需要将流中的元素映射到另一个流中,可以使用 map 方法。
Stream<R> map(Function<? super T, ? extends R> mapper);
sorted方法
如果需要将数据排序,可以使用 sorted 方法。
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
distinct方法
如果需要去除重复数据,可以使用 distinct 方法。
例如:
// 去掉重复,并打印出来
Stream.of(22, 33, 22, 11, 33).distinct().forEach(System.out::println);
如果是自定义类型,根据对象的hashCode和equals来去除重复元素的。
match方法
如果需要判断数据是否匹配指定的条件,可以使用 Match 相关方法。
boolean allMatch(Predicate<? super T> predicate); // allMatch: 元素是否全部满足条件
boolean anyMatch(Predicate<? super T> predicate); // anyMatch: 元素是否任意有一个满足条件
boolean noneMatch(Predicate<? super T> predicate) // noneMatch: 元素是否全部不满足条件
find方法
如果需要找到某些数据,可以使用 find 相关方法。
Optional<T> findFirst();
Optional<T> findAny();
调用optional的get方法即可获得返回值
@Test
public void testFind() {
Optional<Integer> first = Stream.of(5, 3, 6, 1).findFirst();
System.out.println("first = " + first.get());
Optional<Integer> any = Stream.of(5, 3, 6, 1).findAny();
System.out.println("any = " + any.get());
}
max和min方法
Stream流中的 max 和 min 相关方法基本使用的代码如:
@Test
public void testMax_Min() {
Optional<Integer> max = Stream.of(5, 3, 6, 1).max((o1, o2) -> o1 - o2);
System.out.println("first = " + max.get());
Optional<Integer> min = Stream.of(5, 3, 6, 1).min((o1, o2) -> o1 - o2);
System.out.println("any = " + min.get());
}
reduce方法
如果需要将所有数据归纳得到一个数据,可以使用 reduce 方法,他的执行过程如图。

@Test
public void testReduce() {
// 计算总值
int reduce = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
System.out.println("x = " + x + ", y = " + y);
return x + y;
});
System.out.println("reduce = " + reduce); // 21
// 获取最大值
Integer max = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
return x > y ? x : y;
});
System.out.println("max = " + max);
}
maptoInt方法
如果需要将Stream中的Integer类型数据转成int类型,可以使用 mapToInt 方法。
IntStream mapToInt(ToIntFunction<? super T> mapper);
contact方法
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :
@Test
public void testContact() {
Stream<String> streamA = Stream.of("张三");
Stream<String> streamB = Stream.of("李四");
// 合并成一个流
Stream<String> newStream = Stream.concat(streamA, streamB);
// 注意:合并流之后,不能操作之前的流啦.
// streamA.forEach(System.out::println);
newStream.forEach(System.out::println);
}
Stream流的返回结果
将结果整合到集合中
Stream流提供 collect 方法,其参数需要一个 java.util.stream.Collector<T,A, R> 接口对象来指定收集到哪
种集合中。java.util.stream.Collectors 类提供一些方法,可以作为 Collector`接口的实例:
public static <T> Collector<T, ?, List<T>> toList() // 转换为 List 集合。
public static <T> Collector<T, ?, Set<T>> toSet() // 转换为 Set 集合。
下面是这两个方法的基本使用代码:
@Test
public void testStreamToCollection() {
Stream<String> stream = Stream.of("aa", "bb", "cc", "bb");
// 将流中数据收集到集合中
// collect收集流中的数据到集合中
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
// 收集到指定的集合中ArrayList
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet::new));
}
Stream流结果到数组中
Stream<String> stream = Stream.of("aa", "bb", "cc");
String[] strings = stream.toArray(String[]::new);
对流中数据进行聚合计算
当我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作。比如获取最大值,获取最小值,求总和,平均值,统计数量。
对流中数据进行分组
当我们使用Stream流处理数据后,可以根据某个属性将数据分组:
@Test
public void testGroup() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
// 将分数大于60的分为一组,小于60分成另一组
Map<String, List<Student>> map = studentStream.collect(Collectors.groupingBy((s) -> {
if (s.getSocre() > 60) {
return "及格";
} else {
return "不及格";
}
}));
map.forEach((k, v) -> {System.out.println(k + "::" + v); });
}
说明
这个笔记是我学习黑马程序员教程做的笔记,方便自己以后复习查阅。