入门体验
以使用Runnable接口开启多线程举例,使用lambda表达式可以简化代码写法
public class demo {
public static void main(String[] args) {
//匿名内部类写法
Thread t0 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("唱,挑,rap");
}
});
//lambda表达式写法,省略创建匿名对象过程,直接传递方法
//(参数类型 参数名)->{
// 代码块
// }
Thread t1 = new Thread(()->{
System.out.println("唱,挑,rap");
});
}
}
lambda函数式编程思想
函数式编程思想强调做什么,不同面向对象思想,做什么都要以对象的方式进行操作
lambda表达式标准格式
(参数类型 参数名)->{
//代码块。。。
}
lambda表达式简化格式
简化规则
- 小括号内参数类型可以省略
- 小括号内若有且只有一个参数,小括号可以省略
- 大括号内有且仅有一行代码,可以省略大括号、return及分号
根据以上规则,可以将上面开启多线程代码继续简化
Thread t2 = new Thread(()-> System.out.println("唱,挑,rap"));
使用lambda表达式代替匿名内部类写法的前提
使用lambda表达式简化匿名内部类的写法的前提:被替代的原匿名内部类需要实现的接口中必须有且一个抽象方法(可以有其他的default默认方法),比如上面Runnable接口中就只有run一个抽象方法,这种接口就叫函数式接口,这种接口会被@FunctionalInterface注解标记,表示当前类可以使用lambda表达式简化调用写法
lambda表达式的表现形式
- 变量形式:变量的类型为函数接口时,可以使用lambda表达式。如
Runnable runnable = ()->System.out.println("唱,挑,rap");
- 参数形式:方法参数类型为函数接口时,可以使用lambda表达式,如
Thread t2 = new Thread(()-> System.out.println("唱,挑,rap"));
- 返回值形式:返回值类型为函数接口时,可以使用lambda表达式,如
public Runnable getRunnable(){
return ()->System.out.println("唱,挑,rap");
}
stream流
特点:只能使用一次,针对同一stream流进行流处理后,之后就不能再使用该流进行处理了
获取stream流
-
获取Collection集合的stream流
-
获取Map集合的stream流
这里通过map的key、value或key+value获取到的stream流后,就只能针对获取到的stream流进行加工 -
获取数组的stream流
stream流常用api
stream流常用api可以分成两种:
- 终结方法:返回值不再是Stream接口类型的方法,所以调用完这类方法后不再支持链式调用,终结方法包括count和forEach
- 延迟方法:返回值仍然是stream接口类型的方法,所以之后仍然支持链式调用
foreach
针对stream流中每个元素执行特定操作
foreach方法的定义
//返回值为void,属于终结操作
void forEach(Consumer<? super T> action);
foreach参数Consumer类的定义
//FunctionalInterface表示当前接口可以使用lambda表达式简化
@FunctionalInterface
public interface Consumer<T> {
//accept方法中定义针对每个stream元素的遍历操作
void accept(T t);
}
对比stream流foreach下的匿名内部类写法和lambda表达式写法
List<String> list = new ArrayList<>();
list.add("kunkun1");
list.add("kunkun2");
list.add("ikunkun4");
//获取list集合的stream流
Stream<String> stream = list.stream();
//stream流foreach+匿名内部类写法
stream.forEach(new Consumer<String>() {
@Override
public void accept(String name) {
System.out.println(name);
}
});
//stream流foreach+lambda表达式写法
stream.forEach(name->System.out.println(name));
count
统计stream流中元素个数
long count = stream.count();
filter
通过filter可以将一个流根据条件转换为另一个子流
filter方法的定义:
Stream<T> filter(Predicate<? super T> predicate);
参数Predicate类的定义:
//FunctionalInterface表示当前接口可以使用lambda表达式简化
@FunctionalInterface
public interface Predicate<T> {
//参数t代表要过滤的每一个stream元素
//返回值为boolean类型,当前元素在test方法中执行完成后若返回true说明满足过滤条件,进行将当前元素放入stream子流中,否则过滤掉
boolean test(T t);
}
对比stream流filter下的匿名内部类写法和lambda表达式写法
List<String> list = new ArrayList<>();
list.add("kunkun1");
list.add("kunkun2");
list.add("ikunkun4");
//获取list集合的stream流
Stream<String> stream = list.stream();
//使用stream流过滤以k开头的名字
//stream流filter+匿名内部类写法
Stream<String> stream1 = stream.filter(new Predicate<String>() {
@Override
public boolean test(String name) {
return name.startsWith("k");
}
});
//stream流filter+lambda表达式写法
Stream<String> stream2 = stream.filter(name -> name.startsWith("k"));
limit
对stream流进行截取,只取前n个元素返回子流
List<String> list = new ArrayList<>();
list.add("kunkun1");
list.add("kunkun2");
list.add("ikunkun4");
//获取list集合的stream流
Stream<String> stream = list.stream();
//取前2个stream流元素生成子流返回
Stream<String> limit = stream.limit(2);
skip
跳过stream流中前n个元素,生成子流并返回
List<String> list = new ArrayList<>();
list.add("kunkun1");
list.add("kunkun2");
list.add("ikunkun4");
//获取list集合的stream流
Stream<String> stream = list.stream();
//跳过前2个stream流元素生成子流返回
Stream<String> skip= stream.skip(2);
map
将一个流中的元素映射到另一个流中,用于将一个类型的流转换为另一个类型的流,如
将Stream<String>流转换为Stream<Long>流
map方法的定义
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
map方法参数Function类的定义
//FunctionalInterface表示当前接口可以使用lambda表达式简化
@FunctionalInterface
public interface Function<T, R> {
//将T类型的Stream流转换为R类型的Stream流
//方法中定义具体的转换代码
R apply(T t);
比较匿名内部类和lambda两种方式下将String的stream流转换为Long的stream流
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
//获取list集合的stream流
Stream<String> stream = list.stream();
//将String转换为Long类型的Stream流
//stream流map+匿名内部类写法
Stream<Long> objectStream = stream.map(new Function<String, Long>() {
@Override
public Long apply(String s) {
return Long.valueOf(s);
}
});
//stream流map+lambda写法
Stream<Long> longStream = stream.map(s -> Long.valueOf(s));
concat
将两个stream流合并成一个流返回
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
//获取list集合的stream流
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list.stream();
//使用concat将两个流合并成一个
Stream<String> concat = Stream.concat(stream1, stream2);
reduce
用户对stream流中的元素进行累计运算
//累加
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(11);
list.add(12);
//参数1是累加的初始值,参数2是累加的方法引用
Integer result = list.stream().reduce(0, Integer::sum);
collect
用于收集stream流中的结果,将其装入list或set集合中
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
//获取list集合的stream流
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list.stream();
//使用concat将两个流合并成一个
Stream<String> concatStream = Stream.concat(stream1, stream2);
//收集到list集合中
List<String> collect = concatStream.collect(Collectors.toList());
//收集到set集合中
Set<String> collect1 = concatStream.collect(Collectors.toSet());
方法引用
方法引用是针对lambda表达式的进一步简化,当lambda表达式只是在调用某方法,就可使用方法引用进一步简化
这里以Person类举例
public class Person {
String name;
Integer age;
/**
* 比较两对象age的静态方法
* @param o1
* @param o2
* @return
*/
public static int compareByAge(Person o1,Person o2){
return o1.getAge()- o2.getAge();
}
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
静态方法引用举例
//需求:有三个Person对象,要放在一个数组中,并按年龄排序
Person[] people = {new Person("ikun1",10),new Person("ikun1",5),new Person("ikun1",20)};
//匿名内部类写法
Arrays.sort(people, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
//lambda表达式写法
Arrays.sort(people,((o1, o2) -> o1.getAge()- o2.getAge()));
//lambda+静态方法写法,这里的lambda表达式中只有调用某方法的动作,就能用方法引用简化
Arrays.sort(people,((o1, o2) -> Person.compareByAge(o1,o2)));
//lambda+静态方法引用写法,因为lambda表达式所接收的参数列表和静态方法需要接收的参数列表完全一致,所以能够简写,由jdk自动推导
Arrays.sort(people,Person::compareByAge);
静态方法引用格式:ClassName::StaticMethodName
成员方法引用举例
例1:
//需求:通过Supplier获取Person对象的名字
Person person = new Person("ikun",23);
//匿名内部类写法
Supplier supplier = new Supplier<String>() {
@Override
public String get() {
return person.getName();
}
};
//lambda写法
Supplier supplier1 = ()->person.getName();
//成员方法引用写法
Supplier supplier2 = person::getName;
例2:
//需求:遍历并打印出每个集合元素
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
//lambda写法
list.forEach(i->System.out.println(i));
//lambda+实例方法引用写法,System.out返回的是PrintStream类的静态成员
PrintStream printStream = System.out;
list.forEach(printStream::println);
//上面的获取PrintStream对象的代码可以省略,以下面一行代替
list.forEach(System.out::println);
成员方法引用格式:objectName::MethodName
对象方法引用举例
//需求:利用BiPredicate函数式接口用于比较两个String,当两个字符串值相同就认为相等,而不是地址相同
String a = "ab";
String b = "bc";
//匿名内部类写法
BiPredicate<String,String> biPredicate = new BiPredicate<String, String>() {
@Override
public boolean test(String s, String s2) {
return s.equals(s2);
}
};
//lambda表达式写法
BiPredicate<String,String> biPredicate = (s1,s2)->s1.equals(s2);
//lambda表达式+对象方法引用写法
//若lambda表达式第一个参数是方法体中实例方法的调用者,且第二个参数是实例方法的参数,就能使用对象方法引用简化
BiPredicate<String,String> biPredicate = String::equals;
//调用biPredicate对象的test方法进行比较
System.out.println(biPredicate.test(a,b));
对象方法引用格式:ClassName::MethodName
注意与静态方法引用区分:ClassName::StaticMethodName
mybatis plus的lambda
LambdaQueryWrapper
用于构建查询条件,使用举例:
public class TestLog {
@Autowired
AdminMapper adminMapper;
@Test
public void textLog(){
LambdaQueryWrapper<Admin> wrapper = new LambdaQueryWrapper<>();
//匿名内部类写法
wrapper.like(new SFunction<Admin, Object>() {
@Override
public Object apply(Admin admin) {
return admin.getUsername();
}
},"ikun");
//lambda写法
wrapper.like(admin -> admin.getUsername(),"ikun");
//对象方法引用写法,底层会获取到Admin实体类username属性在数据库对应的列名,即查询数据库中Admin实体对应的表的username属性对应的字段值中包含ikun的数据
wrapper.like(Admin::getUsername,"ikun");
List<Admin> admins = adminMapper.selectList(wrapper);
}
}
在开发中,可以这样使用:
wrapper.like(
!StringUtils.isEmpty(username),
Admin::getUsername,
username
);
List<Admin> admins = adminMapper.selectList(wrapper);
第一个参数先判断查询条件是否存在,第二个参数通过方法引用指定列名,第三个参数指定查询条件
LambdaQueryWrapper支持链式编程,其中还有ge(大于查询条件)、le(小于查询条件)、eq(等于查询条件)等方法,用法和like一样
LambdaUpdateWrapper
用于构建修改条件,使用举例
LambdaUpdateWrapper<Admin> wrapper = new LambdaUpdateWrapper<>();
//先看oldUsername有没有值,有值就根据oldUsername的值匹配数据库中username字段,再看newUsername有没有值,有值就用其更新username字段
wrapper.eq(!StringUtils.isEmpty(oldUsername),Admin::getUsername,oldUsername)
.set(!StringUtils.isEmpty(newUsername),Admin::getUsername,newUsername);
adminMapper.update(null,wrapper);
业务中的应用:
假设item.getCount() = 3,item.getSkuId() = 10,则上面的lambda表达式可以翻译为下面的sql,即从sku表中选择id为10的记录,判断记录中的stock字段-locked_stock字段的值是否大于3,若大于3就将locked_stock字段的值+3并返回true,否则不执行修改操作并返回false
update
sku
set
locked_stock=locked_stok+3
where
id = 10 and stock-locked_stock>=3