3.1 Lambda表达式
3.1.1 概念
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升。
3.1.2 匿名内部类
需求:把对象放入集合并按照年龄排序
package com.aaa.jdk8nf.lambda;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
\* @ fileName:Person
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 14:10
\* @ version:1.0.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private int age;
// @Override
// public int compareTo(Person o) {
// return this.age-o.getAge();
// }
}
package com.aaa.jdk8nf.lambda;
import java.util.Comparator;
import java.util.TreeSet;
/**
\* @ fileName:AnonymousInternalClass
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 14:13
\* @ version:1.0.0
*/
public class AnonymousInternalClass {
public static void main(String[] args) {
Person person1 = new Person("赵一",28);
Person person2 = new Person("钱二",18);
Person person3 = new Person("孙三",8);
Person person4 = new Person("李四",38);
TreeSet<Person> personTreeSet = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
personTreeSet.add(person1);
personTreeSet.add(person2);
personTreeSet.add(person3);
personTreeSet.add(person4);
System.out.println(personTreeSet);
}
}
3.1.3 lambda语法及实例
语法及简单示例:
Lmabda表达式的语法总结: () -> {};
λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。
前置 | 语法 |
---|---|
无参数无返回值 | () -> System.out.println(“Hello World”) |
有一个参数无返回值 | (x) -> System.out.println(x) |
有且只有一个参数无返回值 | x -> System.out.println(x) |
有多个参数,有返回值,有多条lambda体语句 | (x,y) -> {System.out.println(“xxx”);return xxxx;}; |
有多个参数,有返回值,只有一条lambda体语句 | (x,y) -> xxxx |
实例:
package com.aaa.nf.lambda;
import java.util.Comparator;
import java.util.TreeSet;
/**
\* @ fileName:TreeSetTest
\* @ description:
\* @ author:zhz
\* @ createTime:2023/2/18 14:41
\* @ version:1.0.0
*/
public class TreeSetTestUseInnerLambdaClass {
public static void main(String[] args) {
Person person1 = new Person("马1云",18);
Person person2 = new Person("马2云",19);
Person person3 = new Person("马3云",14);
Person person4 = new Person("马4云",14);
TreeSet<Person> treeSet = new TreeSet<Person>((Person o1, Person o2) -> o1.getAge()-o2.getAge()==0?o1.getName().hashCode()-o2.getName().hashCode():o1.getAge()-o2.getAge());
// TreeSet<Person> treeSet1 = new TreeSet<Person>((Person o1, Person o2) ->{return o1.getAge()-o2.getAge()==0?o1.getName().hashCode()-o2.getName().hashCode():o1.getAge()-o2.getAge();});
//在TreeSet中添加的对象必须实现比较器接口
treeSet.add(person1);
treeSet.add(person2);
treeSet.add(person3);
treeSet.add(person4);
//ClassCastException 类转换异常
System.out.println(treeSet);
}
}
多线程lambda示例:
package com.aaa.nf.lambda;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
\* @ fileName:MultiThreadLambdaTest
\* @ description:
\* @ author:zhz
\* @ createTime:2023/2/18 15:08
\* @ version:1.0.0
*/
public class MultiThreadLambdaTest {
public static void main(String[] args) {
//匿名内部类写法Runnable
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"启动了。。。。");
}
}).start();
//匿名内部类写法Runnable lambda写法
new Thread(()->System.out.println(Thread.currentThread().getName()+"启动了。。。。")).start();
//匿名内部类写法Callable
new Thread(new FutureTask<>(new Callable<Object>() {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName()+"启动了。。。。");
return 1;
}
})).start();
//匿名内部类写法Callable lambda写法
new Thread(new FutureTask<>(()->{System.out.println(Thread.currentThread().getName()+"启动了。。。。");
return 1;})
).start();
}
}
重要特征及示例:
以下是lambda表达式的重要特征:
-
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
-
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
-
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
-
可选的返回关键字return:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
实现:
package com.aaa.jdk8nf.lambda;
/**
\* @ fileName:CalcInterface
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 14:34
\* @ version:1.0.0
*/
public interface CalcInterface {
/**
\* 运算接口
\* @param a
\* @param b
\* @return
*/
int calc(int a,int b);
}
package com.aaa.jdk8nf.lambda;
/**
\* @ fileName:CalcImpl
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 14:36
\* @ version:1.0.0
*/
public class CalcImpl {
/**
\* 封装运算方法
\* @param a
\* @param b
\* @param calcInterface
\* @return
*/
public static int work(int a,int b, CalcInterface calcInterface){
return calcInterface.calc(a,b);
}
public static void main(String[] args) {
CalcInterface add = new CalcInterface() {
@Override
public int calc(int a, int b) {
return a+b;
}
};
System.out.println(add.calc(1,2));
System.out.println("--------------------------------");
CalcInterface add1 = (a,b) -> a+b;
System.out.println(add1.calc(1,2));
CalcInterface subtract = (a,b)->a-b;
System.out.println(subtract.calc(1,2));
CalcInterface multiple = (a,b)->a*b;
System.out.println(multiple.calc(1,2));
CalcInterface divide = (a,b)->a/b;
System.out.println(divide.calc(4,2));
System.out.println("----------------------------");
System.out.println(work(4,2,(a,b)->a/b));
System.out.println(work(4,2,(a,b)->a+b));
System.out.println(work(4,2,(a,b)->a-b));
}
}
3.1.4 变量作用域
一个局部变量如果要在匿名类或是 Lambda 表达式中访问,那么这个局部变量必须是 final 的,即使没有修饰为 final 类型,编译器也会自动加上 final 修饰符。
package com.aaa.jdk8nf.lambda;
/**
\* @ fileName:CalcImpl
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 14:36
\* @ version:1.0.0
*/
public class CalcImpl {
/**
\* 封装运算方法
\* @param a
\* @param b
\* @param calcInterface
\* @return
*/
public static int work(int a,int b, CalcInterface calcInterface){
return calcInterface.calc(a,b);
}
public static void main(String[] args) {
System.out.println("--------------------------------");
int c = 11;
// CalcInterface add = new CalcInterface() {
// @Override
// public int calc(int a, int b) {
// c = a+b;
// return c;
// }
// };
// System.out.println(add.calc(1,2));
CalcInterface add1 = (a,b) -> {
// c =1;
System.out.println(c+"-------------------------------");
return a+b;
};
System.out.println(add1.calc(1,2));
CalcInterface subtract = (a,b)->a-b;
System.out.println(subtract.calc(1,2));
CalcInterface multiple = (a,b)->a*b;
System.out.println(multiple.calc(1,2));
CalcInterface divide = (a,b)->a/b;
System.out.println(divide.calc(4,2));
System.out.println("----------------------------");
System.out.println(work(4,2,(a,b)->a/b));
System.out.println(work(4,2,(a,b)->a+b));
System.out.println(work(4,2,(a,b)->a-b));
}
}
3.2 函数式接口
3.2.1 概念
一个接口中的抽象方法只有一个,那么这个接口就是一个函数式接口
这种类型的接口也称为SAM接口,即Single Abstract Method interfaces
jdk8 java.util.function包
3.2.2 特点
接口有且仅有一个抽象方法
允许定义静态方法
允许定义默认方法 default
允许含有java.lang.Object中的public方法
@FunctionInterface 该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
3.2.3 常见函数式接口案例
1)supplier
supplier
使用Supplier产生一个指定长度的整型数组
package com.aaa.jdk8nf.sam;
import java.util.Date;
import java.util.Random;
import java.util.function.Supplier;
/**
\* @ fileName:SupplierDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 9:10
\* @ version:1.0.0
*/
public class SupplierDemo {
public SupplierDemo() {
System.out.println(hashCode());
}
/**
\* 使用Supplier产生一个指定长度的整型数组
\* @param length
\* @param integerSupplier
\* @return
*/
public static int[] createFixedLengthIntArrayUseSupplier(int length,Supplier<Integer> integerSupplier){
int[] intArray = new int[length];
for (int i = 0; i < intArray.length; i++) {
intArray[i]=integerSupplier.get();
}
return intArray;
}
public static void main(String[] args) {
Supplier<SupplierDemo> supplierDemoSupplier =()->new SupplierDemo();
SupplierDemo supplierDemo = supplierDemoSupplier.get();
System.out.println(supplierDemo);
Supplier<Date> dateSupplier =()->new Date();
Date date = dateSupplier.get();
System.out.println(date);
System.out.println("--------------------------");
Random random = new Random();
int[] intArray = createFixedLengthIntArrayUseSupplier(20, () -> random.nextInt(100));
for (int i : intArray) {
System.out.print(i+" ");
}
}
}
2) consumer
Consumer
需求:按需要处理字符串并打印
package com.aaa.jdk8nf.sam;
import java.util.function.Consumer;
/**
\* @ fileName:ConsumerDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 9:25
\* @ version:1.0.0
*/
public class ConsumerDemo {
/**
\* 按照需求处理任意字符串
\* @param str
\* @param stringConsumer
*/
public static void handlerString(String str,Consumer<String> stringConsumer){
stringConsumer.accept(str);
}
public static void main(String[] args) {
/* Consumer<String> stringConsumer1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s.toUpperCase());
}
};*/
//转大写打印
Consumer<String> stringConsumer1 = s-> System.out.println(s.toUpperCase());
stringConsumer1.accept("hello");
//获取字符串长度并打印
Consumer<String> stringConsumer2 = s-> System.out.println(s.length());
stringConsumer2.accept("hello");
handlerString("hello world",s-> System.out.println(s.toUpperCase()));
handlerString("hello world",s-> System.out.println(s.length()));
//截取字符串打印
handlerString("hello world",s-> System.out.println(s.substring(0,5)));
}
}
默认实现方法:andThen方法的入参和返回值都是consumer类型的时候,可以使用andThen,在消费数据的时候,多次对数据进行增强处理。
需求:既要把字符串转大写打印,还要计算长度打印,还要截取字符串打印
package com.aaa.jdk8nf.sam;
import java.util.function.Consumer;
/**
\* @ fileName:ConsumerDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 9:25
\* @ version:1.0.0
*/
public class ConsumerAndThenDemo {
/**
\* 按照需求多次处理任意字符串
\* @param str
\* @param stringConsumer
*/
public static void handlerString(String str,Consumer<String> stringConsumer,Consumer<String> stringConsumer1,Consumer<String> stringConsumer2){
stringConsumer.andThen(stringConsumer1).andThen(stringConsumer2).accept(str);
}
public static void main(String[] args) {
// handlerString("hello world",s-> System.out.println(s.toUpperCase()));
// handlerString("hello world",s-> System.out.println(s.length()));
// //截取字符串打印
// handlerString("hello world",s-> System.out.println(s.substring(0,5)));
handlerString("hello world",s-> System.out.println(s.toUpperCase()),
s-> System.out.println(s.length()),
s-> System.out.println(s.substring(0,5)));
}
}
3) predicate
Predicate
需求:判断一个人是否符合做你女/男朋友(根据性别判断)
package com.aaa.jdk8nf.sam;
import java.util.function.Predicate;
/**
\* @ fileName:PredicateDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 9:43
\* @ version:1.0.0
*/
public class PredicateDemo {
/**
\* 判断是否符合做女朋友
\* @param person
\* @param personPredicate
\* @return
*/
public static boolean testIsTrueGetGirlFriend(Person person,Predicate<Person> personPredicate){
return personPredicate.test(person);
}
public static void main(String[] args) {
Person person =new Person("赵漂亮",18,"女");
/*Predicate<Person> personPredicate = new Predicate<Person>() {
@Override
public boolean test(Person person) {
return "女".equals(person.getSex());
}
};*/
Predicate<Person> personPredicate = p->"女".equals(p.getSex());
boolean isTrue = personPredicate.test(person);
System.out.println(isTrue ?"符合":"不符合");
System.out.println("---------------------------");
boolean isTrue1 =testIsTrueGetGirlFriend(person,p->"女".equals(p.getSex()));
System.out.println(isTrue1 ?"符合":"不符合");
}
}
and方法 逻辑与 多个条件共同满足。
package com.aaa.jdk8nf.sam;
import java.util.function.Predicate;
/**
\* @ fileName:PredicateDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 9:43
\* @ version:1.0.0
*/
public class PredicateAndDemo {
/**
\* 判断是否符合做女朋友 and多个条件共同判断
\* @param person
\* @param personPredicate
\* @return
*/
public static boolean testIsTrueGetGirlFriend(Person person,Predicate<Person> personPredicate
,Predicate<Person> personPredicate1
,Predicate<Person> personPredicate2){
return personPredicate.and(personPredicate1).and(personPredicate2).test(person);
}
public static void main(String[] args) {
Person person =new Person("赵漂亮",18,"女");
boolean isTrue1 =testIsTrueGetGirlFriend(person,p->"女".equals(p.getSex()),
p->p.getAge()>=18,
p->p.getName().contains("漂亮"));
System.out.println(isTrue1 ?"符合":"不符合");
}
}
or方法 逻辑或,满足任一条件即可。
package com.aaa.jdk8nf.sam;
import java.util.function.Predicate;
/**
\* @ fileName:PredicateDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 9:43
\* @ version:1.0.0
*/
public class PredicateOrDemo {
/**
\* 判断是否符合做女朋友 and多个条件共同判断
\* @param person
\* @param personPredicate
\* @return
*/
public static boolean testIsTrueGetGirlFriend(Person person,Predicate<Person> personPredicate
,Predicate<Person> personPredicate1
,Predicate<Person> personPredicate2){
return personPredicate.or(personPredicate1).or(personPredicate2).test(person);
}
public static void main(String[] args) {
Person person =new Person("赵美丽",18,"男");
boolean isTrue1 =testIsTrueGetGirlFriend(person,p->"女".equals(p.getSex()),
p->p.getAge()>=18,
p->p.getName().contains("漂亮"));
System.out.println(isTrue1 ?"符合":"不符合");
}
}
4) function
function<T,R> 函数型(转换行)接口,有参数T,有返回值R,对类型T对象操作,返回R类型的对象。
需求:输入一个对象集合,转换获取一个对象名称集合
package com.aaa.jdk8nf.sam;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
/**
\* @ fileName:FunctionDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 16:09
\* @ version:1.0.0
*/
public class FunctionDemo {
/**
\* 把人员集合转换为人员名称集合
\* @param personList
\* @param personStringFunction
\* @return
*/
public static List<String> personListToPersonNameList(List<Person> personList, Function<Person,String> personStringFunction){
List<String> nameStringList = new ArrayList<>();
for (Person person : personList) {
nameStringList.add(personStringFunction.apply(person));
}
return nameStringList;
}
public static void main(String[] args) {
Person person1 = new Person("赵美丽1",29,"女");
Person person2 = new Person("赵美丽2",19,"女");
Person person3 = new Person("赵美丽3",39,"女");
Person person4 = new Person("赵美丽4",9,"女");
Person person5 = new Person("赵美丽5",18,"女");
List<Person> personList = new ArrayList<>();
personList.add(person1);
personList.add(person2);
personList.add(person3);
personList.add(person4);
personList.add(person5);
List<String> stringList = personListToPersonNameList(personList, p -> p.getName());
System.out.println(stringList);
}
}
compose和andThen 都是多个处理函数的组合,区别是在当前函数执行前后
package com.aaa.jdk8nf.sam;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
/**
\* @ fileName:FunctionDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 10:01
\* @ version:1.0.0
*/
public class FunctionComposeAndThenDemo {
/**
\* 测试compose和andthen区别
\* @param i
\* @param integerFunction1
\* @param integerFunction2
\* @return
*/
public static int composeExecute(int i,Function<Integer,Integer> integerFunction1,
Function<Integer,Integer> integerFunction2){
//在调用Function1之前会先调用Function2
return integerFunction1.compose(integerFunction2).apply(i);
}
/**
\* 测试compose和andthen区别
\* @param i
\* @param integerFunction1
\* @param integerFunction2
\* @return
*/
public static int andThenExecute(int i,Function<Integer,Integer> integerFunction1,
Function<Integer,Integer> integerFunction2){
//在调用Function1之后再调用Function2
return integerFunction1.andThen(integerFunction2).apply(i);
}
public static void main(String[] args) {
int i1 = composeExecute(3, i -> 2 * i, i -> i * i);
System.out.println(i1);
int i2 = andThenExecute(3, i -> 2 * i, i -> i * i);
System.out.println(i2);
}
}
3.2.4 自定义函数式接口
1)定义接口
package com.aaa.jdk8nf.sam;
/**
\* @ fileName:CustomPrintMessage
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 10:42
\* @ version:1.0.0
*/
@FunctionalInterface
public interface CustomPrintMessage<A> {
/**
\* 打印任何方法
\* @param t
*/
void printAnyThing(A t);
//void printAnyThing1(A t);
/**
\* 可以有使用default修饰的方法
*/
default void mehthodA(){
System.out.println("可以有default方法");
};
/**
\* 可以有使用static的方法
*/
static void methodB(){
System.out.println("可以有static方法");
}
/**
\* 也可以含有java.lang.Object的public方法
\* @param obj
\* @return
*/
boolean equals(Object obj);
/**
\* 也可以含有java.lang.Object的public方法
\* @return
*/
String toString();
}
2)接口使用:
package com.aaa.jdk8nf.sam;
/**
\* @ fileName:CustomPrintMessageTest
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 10:47
\* @ version:1.0.0
*/
public class CustomPrintMessageTest {
/**
\* 自定义函数接口的使用
\* @param person
\* @param personCustomPrintMessage
*/
public static void printPerson(Person person,CustomPrintMessage<Person> personCustomPrintMessage){
personCustomPrintMessage.printAnyThing(person);
}
public static void main(String[] args) {
Person person1 =new Person("赵漂亮1",28,"女");
printPerson(person1,p-> System.out.println(p));
}
}
3.3 方法引用
3.3.1 概念及作用
概念:
方法引用是jdk8推出的一个新特性,在一定的条件下可以替换lambda表达式,可以理解为方法引用实际上还是一个lambda表达式。
作用:
旨在编写更加简洁紧凑的代码风格,提高代码重用率(不提高执行效率)。
3.3.2 语法 ::
类型Class | 方法引用 | lambda表达式 |
---|---|---|
静态方法引用 | 类名::method | (args)->类名.method(args) |
实例方法引用 | 实例::method | (args)->实例.method(args) |
实例方法引用 | 类名::method | (inst,args)->类名.method(args) |
构造方法引用 | 类名::new | (args)->new 类名(args) |
(函数式)接口类型 对象变量名 = 对象::被引用的实例方法;
(函数式)接口类型 对象变量名 = 类名::被引用的静态方法;
(函数式)接口类型 对象变量名 = 类名::被引用的实例方法;
(函数式)接口类型 对象变量名 = 类名::new(引用的是类的构造方法);
“=”号左边函数式接口抽象方法的参数列表、返回值类型需要与右边被引用的方法的参数列表、返回值类型一致。
注意:方法引用旨在简化lambda表达式写法,在lambda表达式的基础上使用,不能脱离lambda表达式单独使用(lambda表达式依赖函数式接口)。
3.3.3 代码示例
person:
package com.aaa.jdk8nf.methodref;
import java.util.function.Supplier;
/**
\* @ fileName:Person
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 16:39
\* @ version:1.0.0
*/
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
\* 借助供给型接口Supplier 创建Person对象
\* @param personSupplier
\* @return
*/
public static Person createPerson(Supplier<Person> personSupplier){
return personSupplier.get();
}
/**
\* 静态打印方法
\* @param person
*/
public static void staticPrint(Person person){
System.out.println(person);
}
/**
\* 带参普通打印方法
\* @param person
*/
public void print(Person person){
System.out.println(person);
}
/**
\* 不带参数普通打印方法
*/
public void print(){
System.out.println("不带参数打印方法");
}
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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试
package com.aaa.jdk8nf.methodref;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
/**
\* @ fileName:MethodReferenceDemo
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 16:44
\* @ version:1.0.0
*/
public class MethodReferenceDemo {
public static void main(String[] args) {
//类::new 调用构造方法
System.out.println("--------------使用lambda表达式调用构造方法--------------");
Person person1 = Person.createPerson(()->new Person());
System.out.println(person1);
System.out.println("--------------使用方法引用调用构造方法--------------");
Person person2 = Person.createPerson(Person::new);
//Person person3 = Person::new; //错误 必须在lambda基础上使用 不能单独使用
System.out.println(person2);
/*BiFunction<String,Integer,Person> biFunction1 = new BiFunction<String, Integer, Person>() {
@Override
public Person apply(String s, Integer integer) {
return new Person(s,integer);
}
};*/
// BiFunction<String,Integer,Person> biFunction2 = (s,i)->new Person(s,i);
// BiFunction 两个输入参数 一个返回值 和当前构造匹配 就可以使用它,进行方法引用调用
BiFunction<String,Integer,Person> biFunction = Person::new;
Person person3 = biFunction.apply("王五1", 18);
Person person4 = biFunction.apply("王五2", 28);
Person person5 = biFunction.apply("王五3", 38);
List<Person> personList = new ArrayList<>();
personList.add(person3);
personList.add(person4);
personList.add(person5);
//类::静态方法
System.out.println("--------------使用lambda表达式调用静态方法--------------");
personList.forEach(p-> System.out.println(p));
System.out.println("--------------使用方法引用调用静态方法--------------");
personList.forEach(Person::staticPrint);
//对象::普通方法
System.out.println("-------使用方法引用调用静态方法System.out.print---------");
/*PrintStream out = System.out;
personList.forEach(out::println);*/
personList.forEach(System.out::println);
System.out.println("--------------使用方法引用调用非静态方法--------------");
personList.forEach(person5::print);
//类::普通方法
System.out.println("--------------使用方法引用调用非静态方法--------------");
personList.forEach(Person::print);
}
}
3.4 stream API
3.4.1 概念及作用
概念:
Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作,在新版的JPA【连接数据库】中,也已经加入了Stream。
作用:
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
注意事项:
一个stream只能操作一次
stream流返回的是新的流
只有调用终结方法stream的中间操作才会执行
3.4.2 流生成
1)stream:所有的 Collection 集合都可以通过 stream 默认方法获取流(顺序流);
2)parallelStream:所有的 Collection 集合都可以通过parallelStream获取并行流
3)Stream.of:Stream 接口的静态方法 of 可以获取数组对应的流。
4)Arrays.stream: Arrays的静态方法stream也可以获取流
package com.aaa.jdk8nf.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/10 11:33
\* @ version:1.0.0
*/
public class StreamCreate {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("zhaoyi");
stringList.add("qianer");
stringList.add("sunsan");
stringList.add("lisi");
//打印
stringList.forEach(System.out::println);
//1,任何集合对象中都有stream()
Stream<String> stream = stringList.stream();
//2,任何集合对象中都有 parallelStream()
Stream<String> stringStream = stringList.parallelStream();
//3,通过Stream 的of方法产生
Stream<String> stream1 = Stream.of("a", "b", "c", "d");
//4,通过Arrays工具类 产生类
String[] strArrray = {"a", "b", "c", "d"};
Stream<String> stream2 = Arrays.stream(strArrray);
}
}
3.4.3 常用方法
1)forEach
终结方法 用来遍历流中的数据
需求: 循环遍历StringList集合
package com.aaa.newfeature.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamForEach {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("zhaoyi");
stringList.add("qianer");
stringList.add("sunsan");
stringList.add("lisi");
Stream<String> stream = stringList.stream();
stream.forEach(s-> System.out.println(s));
stream.forEach(System.out::println);
}
}
2)map
非终结方法 将流中的元素映射到另一个流中。该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
需求:把所有用户名转大写并输出
package com.aaa.newfeature.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamMap {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("zhaoyi");
stringList.add("qianer");
stringList.add("sunsan");
stringList.add("lisi");
Stream<String> stream = stringList.stream().map((s)->{return s.toUpperCase();});
stream.forEach(System.out::println);
stream.close();
}
}
3)filter
非终结方法 用于过滤数据,返回符合过滤条件的数据,将一个流转换成另一个子集流。
需求:找出人员表中年龄大于18岁的人,并打印
package com.aaa.newfeature.stream;
import com.aaa.newfeature.methodref.Person;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamFilter {
public static void main(String[] args) {
//调用带参构造
//BiFunction表示接受两个参数并产生结果的函数
BiFunction<String,Integer, Person> personBiFunction=Person::new;
Person person1 = personBiFunction.apply("孙三", 10);
Person person2 = personBiFunction.apply("李四", 20);
Person person3 = personBiFunction.apply("王五", 12);
Person person4 = personBiFunction.apply("马六", 21);
List<Person> personList = new ArrayList<>();
personList.add(person1);
personList.add(person2);
personList.add(person3);
personList.add(person4);
Stream<Person> stream = personList.stream().filter(p->{return p.getAge()>18;});
stream.forEach(System.out::println);
stream.close();
}
}
4)limit
非终结方法 limit(int maxSize) 返回由此流的元素组成的流,截短长度不能超过 maxSize 。
package com.aaa.newfeature.stream;
import com.aaa.newfeature.methodref.Person;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamLimit {
public static void main(String[] args) {
//调用带参构造
//BiFunction表示接受两个参数并产生结果的函数
BiFunction<String,Integer, Person> personBiFunction=Person::new;
Person person1 = personBiFunction.apply("孙三", 10);
Person person2 = personBiFunction.apply("李四", 20);
Person person3 = personBiFunction.apply("王五", 12);
Person person4 = personBiFunction.apply("马六", 21);
List<Person> personList = new ArrayList<>();
personList.add(person1);
personList.add(person2);
personList.add(person3);
personList.add(person4);
Stream<Person> stream = personList.stream().limit(3);
stream.forEach(System.out::println);
stream.close();
}
}
5)sorted
非终结方法 返回由此流的元素组成的流,根据自然顺序排序。
package com.aaa.newfeature.stream;
import com.aaa.newfeature.methodref.Person;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamSorted {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("zhaoyi");
stringList.add("qianer");
stringList.add("sunsan");
stringList.add("lisi");
Stream<String> stream = stringList.stream().sorted();
//stream.forEach(s-> System.out.println(s));
stream.forEach(System.out::println);
stream.close();
System.out.println("-------------------------------------------");
//调用带参构造
//BiFunction表示接受两个参数并产生结果的函数
BiFunction<String,Integer, Person> personBiFunction=Person::new;
Person person1 = personBiFunction.apply("孙三", 10);
Person person2 = personBiFunction.apply("李四", 20);
Person person3 = personBiFunction.apply("王五", 12);
Person person4 = personBiFunction.apply("马六", 21);
List<Person> personList = new ArrayList<>();
personList.add(person1);
personList.add(person2);
personList.add(person3);
personList.add(person4);
personList.stream().sorted((p1,p2)->{return p1.getAge()>p2.getAge()?1:p1.getAge()<p2.getAge()?-1:0;}).forEach(System.out::println);
}
}
6)collect
终结方法 把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。collect主要依赖java.util.stream.Collectors类内置的静态方法。
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。
package com.aaa.jdk8nf.stream;
import com.aaa.jdk8nf.methodref.Person;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/10/12 17:17
\* @ version:1.0.0
*/
public class StreamCollect {
public static void main(String[] args) {
BiFunction<String,Integer, Person> biFunction = Person::new;
Person person3 = biFunction.apply("王五1", 48);
Person person4 = biFunction.apply("王五2", 28);
Person person5 = biFunction.apply("王五3", 8);
Person person1 = biFunction.apply("王五4", 17);
Person person2 = biFunction.apply("王五5", 38);
Person person6 = biFunction.apply("李五5", 38);
List<Person> personList = new ArrayList<>();
personList.add(person3);
personList.add(person4);
personList.add(person5);
personList.add(person1);
personList.add(person2);
personList.add(person6);
Stream<Person> personStream1 = personList.stream();
List<Person> resultPersonList = personStream1.filter(p -> p.getAge() >= 18 && p.getName().startsWith("王"))
.sorted((p1, p2) -> p1.getAge() - p2.getAge()).limit(3).collect(Collectors.toList());
for (Person person : resultPersonList) {
System.out.println(person);
}
System.out.println("----------------------------------");
Stream<Person> personStream2 = personList.stream();
Map<String, Person> personMap = personStream2.filter(p -> p.getAge() >= 18 && p.getName().startsWith("王"))
.sorted((p1, p2) -> p1.getAge() - p2.getAge()).limit(3).collect(Collectors.toMap(Person::getName, p -> p));
System.out.println(personMap);
}
}
7)count
终结方法 统计其中的元素个数
package com.aaa.newfeature.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamCount {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("zhaoyi");
stringList.add("qianer");
stringList.add("sunsan");
stringList.add("lisi");
Stream<String> stream = stringList.stream();
//stream.forEach(s-> System.out.println(s));
long count = stream.count();
System.out.println(count);
stream.close();
}
}
8)skip
非终结方法 跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流
package com.aaa.newfeature.stream;
import com.aaa.newfeature.methodref.Person;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamSkip {
public static void main(String[] args) {
//调用带参构造
//BiFunction表示接受两个参数并产生结果的函数
BiFunction<String,Integer, Person> personBiFunction=Person::new;
Person person1 = personBiFunction.apply("孙三", 10);
Person person2 = personBiFunction.apply("李四", 20);
Person person3 = personBiFunction.apply("王五", 12);
Person person4 = personBiFunction.apply("马六", 21);
List<Person> personList = new ArrayList<>();
personList.add(person1);
personList.add(person2);
personList.add(person3);
personList.add(person4);
Stream<Person> stream = personList.stream().skip(2);
stream.forEach(System.out::println);
stream.close();
}
}
9) find
findFirst: 从流中获取一个元素(一般情况下,是获取的开头的元素)
findAny: 从流中获取一个元素(一般情况下,是获取的开头的元素)
这两个方法,绝大部分情况下,是完全相同的,但是在多线程的环境下,findAny和find返回的结果可能不一样。
package com.aaa.newfeature.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
\* @ fileName:StreamCreate
\* @ description:
\* @ author:zhz
\* @ createTime:2022/9/26 21:55
\* @ version:1.0.0
*/
public class StreamFind {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("zhaoyi");
stringList.add("qianer");
stringList.add("sunsan");
stringList.add("lisi");
//stream.forEach(s-> System.out.println(s));
String s = stringList.stream().findFirst().get();
System.out.println(s);
Optional<String> any = stringList.stream().findAny();
System.out.println(any.get());
}
}
等等
4,知识点总结
5,本章面试题
为什么 Lambda 表达式(匿名类) 不能访问非 final 的局部变量呢?
因为实例变量存在堆中,而局部变量是在栈上分配,Lambda 表达式(匿名类) 会在另一个线程中执行。如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了,而 final 类型的局部变量在 Lambda 表达式(匿名类) 中其实是局部变量的一个拷贝。
在java编译时,匿名内部类也会被当作普通的类处理,只不过编译器生成它构造方法的时候,除了将外部类的引用传递了过来,还将基本数据类型的变量复制了一份过来,并把引用数据类型的变量引用也传递了过来。因此,基本数据类型的变量当然不能修改了,不然就会跟外部的变量产生不一致,这样的话变量的传递也就变得毫无意义了。
标签:JDK1.8,System,特性,Person,println,import,public,out From: https://www.cnblogs.com/zcf94264/p/17170650.html