Lambda 表达式和 Stream 流
1. Lambda 表达式
1.1 Lambda 简介
JDK 1.8 之后的新特性
平常的方法参数都是引用型数据类型或者基本数据类型
Lambda 允许把函数接口作为一个方法的参数 (函数作为参数传递进方法中)
Lambda 可以使代码更加简洁,优化代码
1.2 Lambda 格式
(参数) -> {方法体};
三个一,一个小括号,一个箭头,一个大括号
1. 在参数有且仅有一个情况下 () 可以省略
2. 在方法体中执行语句有且只有一行的情况下可以省略 return ; 和 {}
1.3 匿名内部类与 Lambda 表达式
首先我们要明白对于方法 我们要在意的是 怕【参数】 和 【返回值】,方法名并不重要
1.3.1 无参无返回值
interface Test1 {
void test();
}
public class Demo1 {
public static void main(String[] args) {
// 匿名内部类方式
testLambda(new Test1() {
@Override
public void test() {
System.out.println("匿名内部类方式");
}
});
// Lambda 表达式
testLambda(() -> System.out.println("Lambda 方式"));
}
public static void testLambda(Test1 test1) {
test1.test();
}
}
1.3.2 无参有返回值
interface Supplier<T> {
T get();
}
public class Demo3 {
public static void main(String[] args) {
// 匿名内部类方式
String s = testLambda(new Supplier<String>() {
@Override
public String get() {
return "这是一个字符串";
}
});
System.out.println(s);
// Lambda 表达式方式
String s1 = testLambda(() -> "这也是一个字符串");
System.out.println(s1);
}
public static String testLambda(Supplier<String> s){
return s.get();
}
}
1.3.3 有参无返回值
/**
* 自定义的一个消费者接口
*/
@FunctionalInterface
interface Consumer<T> {
void accept(T t);
}
public class Demo2 {
public static void main(String[] args) {
// 匿名内部类方式
testLambda("春天在哪里", new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// Lambda 表达式方式
testLambda("春天在哪里", s -> System.out.println(s));
}
public static void testLambda(String str, Consumer<String> handle) {
handle.accept(str);
}
}
1.3.4 有参有返回值
/**
* 自定义比较器接口
*/
@FunctionalInterface
interface Comparator<T> {
int compare(T o1, T o2);
}
public class Demo4 {
public static void main(String[] args) {
Person[] array = new Person[5];
Random random = new Random();
for (int i = 0; i < array.length; i++) {
// 随机数范围 1 ~ 50
array[i] = new Person(i + 1, "张三", random.nextInt(50) + 1);
}
匿名内部类方式
sortPersonArrayUsingComparator(array, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
for (Person person : array) {
System.out.println(person);
}
// Lambda 表达式
sortPersonArrayUsingComparator(array, (p1, p2) -> p1.getId() - p2.getId());
for (Person person : array) {
System.out.println(person);
}
}
public static void sortPersonArrayUsingComparator(Person[] array, Comparator<Person> comparator) {
for (int i = 0; i < array.length - 1; i++) {
int index = i;
for (int j = i + 1; j < array.length; j++) {
// compare 方法的返回值是 int 类型但是值很特殊 > 0 表示前者大 < 0 表示后者大 = 0 表示相等
// 我们利用这个判断 如果大于零 交换下标
if (comparator.compare(array[index], array[j]) > 0){
index = j;
}
}
if (index != i) {
Person temp = array[i];
array[i] = array[index];
array[index] = temp;
}
}
}
}
2. Stream 流
2.1 Stream 流概述
JDK 1.8 版本新特性
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
2.2 如何得到 Stream 流对象
Stream 流处理的是集合 或者 数组
对于集合
Stream<T> stream();
直接通过 集合对象.Stream(); 来获取流对象。
对于数组
Stream<T> Arrays.Stream(T[] t);
利用 Arrays 工具类调用 Stream(T[] t); 方法参数为想要获取修的数组参数。
Stream 处理数据的 【中间方法】 返回值还是 Stream 流对象的 称为【中间方法】
常用方法
Stream<T> skip(long n);
限制跳过当前 Stream 流对应元素的个数,【掐头】
Stream<T> limit(long n);
限制当前 Stream 对应的元素总个数,【去尾】
Stream<T> sorted();
对当前 Stream 流存储的进行排序操作,要求元素有自然顺序或者遵从 Comparable 接口,默认【升序】
Stream<T> sorted(Comparator<? super T> com);
对当前 Stream 流存储的进行排序操作,排序规则由 Comparator 函数式接口规范
Stream<T> filter(Predicate<? super T> pre);
判断过滤当前 Stream 流可以保存的数据条件,满足条件保留,不满足条件移除,过滤规则由 Predicate 接口约束
Stream<T> distinct();
当前 Stream 流中对应的所有元素去重擦操作
Stream<R> map(Function<T, R> fun);
当前 Stream 存储的数据情况转换为 Function 函数式接口要求的返回值类型,完成类型转换操作。
Stream 处理数据的【终止方法】流调用这些方法后返回值类型不再是 Stream 流对象
Stream 流会自动关闭,对应的 Stream 占用的资源空间会被 JVM 收回
long count();
返回当前 Stream 流对应的数据元素个数,为终止方法。
void forEach(Consumer<? super T> con);
针对于当前 Stream 存储元素的处置方法,为终止方法。
<R, A> R collect(Collector<? super T, A, R> collector);
Stream 流对应的元素存储内容,转换为用户要求的 集合对象。终止方法
常用:
Collectors.toList() 目标存储集合类型为 List 集合
Collectors.toSet() 目标存储集合类型为 Set 集合
Object[] toArray();
Stream 流存储的元素内容转换为 Object 类型数组返回
2.3 代码演示演示
【中间】方法演示
/**
* Person 为自定义类 属性为 private Integer id;
private String name;
private Integer age;
*/
public class Demo6 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
list.add(new Person( i + 1, "张三", random.nextInt(50) + 1));
}
list.stream()
.skip(2)
.limit(8)
/* 这里使用了 Lambda 表达式 参数为 Comparator 接口需要我们实现的方法为
* compare 方法 参数为 两个
* 返回值为 int 类型
*/
.sorted((p1, p2) -> p1.getAge() - p2.getAge())
/* 这里使用了 Lambda 表达式 参数为 Predicate 接口 需要我们实现的方法为
* test 方法 参数为 1个
* 返回值为 boolean 类型
*/
.filter(p -> p.getAge() > 20)
// forEach 为最终方法执行后 Stream 流自动关闭
.forEach(System.out::println);
}
}
public class Demo7 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("巴巴托斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("雷电影");
list.add("雷电影");
list.add("雷电影");
list.add("雷电影");
list.add("雷电真");
list.add("小吉祥草王");
list.add("大慈树王");
list.add("大慈树王");
list.add("大慈树王");
list.add("大慈树王");
list.add("灶神马科修斯");
list.add("流云借风真君");
list.add("赤王");
list.add("原初的那一位");
list.stream()
// 去重
.distinct()
/* 这里使用了 Lambda 表达式 参数为 Function 接口 需要我们实现的方法为
* apply 方法 参数为 1个
* 返回值为 你需要转换的类型
*/
.map(s -> s.toCharArray())
.forEach(System.out::println);
}
}
【最终】方法
public class Demo8 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("巴巴托斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("雷电影");
list.add("雷电影");
list.add("雷电影");
list.add("雷电影");
list.add("雷电真");
list.add("小吉祥草王");
list.add("大慈树王");
list.add("大慈树王");
list.add("大慈树王");
list.add("大慈树王");
list.add("灶神马科修斯");
list.add("流云借风真君");
list.add("赤王");
list.add("原初的那一位");
Stream<String> stream = list.stream();
/* collect 为最终方法 Collectors.toSet() 将数据存入 set 集合中
set 集合 无序 且 元素不可重复 所以 输出结果会自动去重 且 和添加顺序不一样
*/
Set<String> collect = stream.collect(Collectors.toSet());
for (String s : collect) {
System.out.println(s);
}
/*
报错 stream has already been operated upon or closed
Stream 流已经被执行或者关闭
因为 collect 为最终方法 执行完毕 流自动关闭无法在使用
*/
stream.filter(s -> s.length() <= 4).forEach(System.out::println);
}
}
public class Demo9 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("巴巴托斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("摩拉克斯");
list.add("雷电影");
list.add("雷电影");
list.add("雷电影");
list.add("雷电影");
list.add("雷电真");
list.add("小吉祥草王");
list.add("大慈树王");
list.add("大慈树王");
list.add("大慈树王");
list.add("大慈树王");
list.add("灶神马科修斯");
list.add("流云借风真君");
list.add("赤王");
list.add("原初的那一位");
// 将集合中元素放入 Object[] 数组中
Object[] array = list.stream().distinct().toArray();
for (Object o : array) {
System.out.println(o);
}
System.out.println();
// 计算集合中元素个数
long count = list.stream().count();
System.out.println(count); // 20
}
}