文章目录
一、Lambda表达式
lambda表达式是jdk8中的新特性之一,对某些匿名内部类的写法进行了简化。将能省的东西都省掉了。
首先看一下我们原本创建线程常规写法应该怎么写
1.创建一个类去实现Runnable接口,重写run方法。
2.创建Thread对象,将实现了Runnable接口的对象作为Thread对象的参数
3.调用start方法启动线程,具体代码如下:
//创建实现Runnable的类
public class ThreadText implements Runnable{
@Override
public void run() {
System.out.println("我是123 ");
}
public static void main(String[] args) {
//创建实现Runnable接口类的对象
ThreadText threadText=new ThreadText();
//将此对象传作为参数创建Thread对象
Thread thread=new Thread(threadText);
//启动线程
thread.start();
}
}
换一种写法,使用匿名内部类来实现
public class ThreadText {
public static void main(String[] args) {
//匿名内部类
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是456");
}
});
thread.start();
}
}
在换一种写法 ,使用Lambda表达式:
public class ThreadText {
public static void main(String[] args) {
//匿名内部类
Thread thread=new Thread(()->{
System.out.println("我是789 " );
});
thread.start();
}
}
你会发现,是不是突然看不懂了,和匿名内部类相比,Lambda表达式有什么不同
可以看见,方框中的东西被胜率掉了,在使用Lambda表达式时,只关注方法的参数和方法体,其他的东西都可以省略。
1.1 什么情况下可以使用Lambda表达式
看看Runnable源码
一个接口里面,只有一个抽象方法,就可以在将该接口当做匿名内部类使用时,使用Lambda表达。
1.2 Lambda表达式的格式
- (参数列表)->{代码}
- Lambda表达式只关注参数列表和方法体,省略类名和方法的定义
- 可以使用IDEA的快捷键 ALT+回车来进行匿名内部类和Lambda表达式的转换
1.3 Lambda表达式的省略规则
- 参数类型可以省略
- 方法体只有一句代码时大括号return和唯一一句代码的分号可以省略
- 方法只有一个参数时小括号可以省略
二、Stream流
2.1 创建流对象(Stream)
2.1.1单列集合
集合对象.stream()
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Stream<Integer> stream = list.stream();
stream.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer>2;
}
}).forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println("integer = " + integer);
}
});
}
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.stream()
.filter(integer -> {
return integer > 2;
})
.forEach(integer -> {
System.out.println("integer = " + integer);
});
}
2.1.2 数组
通过Arrays.stream(数组对象)
或者 Stream.of(数组对象)
public static void main(String[] args) {
Integer[] arr = {15, 16, 17, 20};
//通过Arrarys.stream()
Stream<Integer> stream = Arrays.stream(arr);
stream.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer > 17;
}
}).forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println("Arrarys.stream()创建的流,经过过滤之后的元素有:value = " + integer);
}
});
//通过Stream.of
Stream<Integer> streamOf = Stream.of(arr);
streamOf.
filter(id -> {
return id > 15;
})
.forEach(num -> {
System.out.println("Stream.of()创建的流,经过过滤之后的元素有:num = " + num);
});
}
2.1.3 双列集合
先将双列集合转换为单列集合
HashMap<Object,Integer> map=new HashMap<>();
map.put("张三",15);
map.put("李四",16);
//转换为单列集合
Set<Map.Entry<Object, Integer>> entries = map.entrySet();
Stream<Map.Entry<Object, Integer>> stream = entries.stream();
stream.filter(p->{
return p.getValue()>15;
}).forEach(map1 -> {
System.out.println(map1.getKey()+":++++++++++"+map1.getValue());
});
}
2.2 流对象的中间操作
2.2.1 filter
过滤,满足条件的元素才能继续留在流中,前面演示的代码已经体现了filter的作用。
2.2.2 map
将流中的元素进行计算或者转换,如果流是一个实体类,可以通过map给实体类中的某些属性赋值。
实体类
package com.example.demo.config;
import java.util.Objects;
public class User {
private String name;
private Integer age;
private String email;
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;
}
public Integer getEmail() {
return email;
}
public void setEmail(Integer email) {
this.email = email;
}
public User() {
}
public User(String name, Integer age, Integer email) {
this.name = name;
this.age = age;
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return name.equals(user.name) && age.equals(user.age) && email.equals(user.email);
}
@Override
public int hashCode() {
return Objects.hash(name, age, email);
}
}
初始化数据
public static void main(String[] args) {
List<User> list =new ArrayList<>();
list.add(new User("张三",18,null));
list.add(new User("李四",18,null));
list.add(new User("王二",18,null));
list.add(new User("麻子",18,null));
}
通过map,我们就可以让给他们加上email的值
public static void main(String[] args) {
List<User> list =new ArrayList<>();
list.add(new User("张三",18,null));
list.add(new User("李四",18,null));
list.add(new User("王二",18,null));
list.add(new User("麻子",18,null));
list.stream().map(user->{
//如果年龄等于18,则给Email赋值
if(user.getAge()==18){
user.setEmail("162@.com");
}
//将操作完的对象返回流中
return user;
//通过forEach遍历
}).forEach(user->{
System.out.println("user = " + user.toString());
});
}
可以看到,每一个对象都被赋予了email的值,因为他们的age都等于18
还可以清空麻子的年龄
public static void main(String[] args) {
List<User> list =new ArrayList<>();
list.add(new User("张三",18,null));
list.add(new User("李四",18,null));
list.add(new User("王二",18,null));
list.add(new User("麻子",18,null));
list.stream().map(user->{
//如果年龄等于18,则给Email赋值
if(user.getName().equals("麻子")){
user.setEmail(null);
user.setAge(null);
}
//将操作完的对象返回流中
return user;
//通过forEach遍历
}).forEach(user->{
System.out.println("user = " + user.toString());
});
}
总之map操作,在项目中是比较常用的。
2.2.3 distinct
用于去除流中重复的元素
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 18, "123@.com"));
list.add(new User("李四", 18, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.stream()
.distinct()
.forEach(user -> {
System.out.println("user = " + user.toString());
});
}
使用distinct时,务必重写equals和hashcode方法。
2.2.4 sorted
用于对流中的元素进行排序
例如,按照年龄进行降序排序
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("王二", 45, "123@.com"));
list.stream().sorted((user1,user2)->{
return user2.getAge()- user1.getAge();
})
.forEach(user -> {
System.out.println("user = " + user.toString());
});
}
升序为第一个参数减第二个参数,降序为第二个参数减第一个参数。
2.2.5 limit
限制流的长度
例如,按照年龄升序排序,只取前两个元素
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("王二", 45, "123@.com"));
list.stream()
.sorted((user1, user2) -> {
return user1.getAge() - user2.getAge();
})
.limit(2)
.forEach(user -> {
System.out.println("user = " + user.toString());
});
}
2.2.6 skip
skip和limit相反,skip是跳过前n个元素,返回剩下的元素
例如,按照年龄升序排序,去掉重复值,取后两个元素
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
list.stream()
.sorted((user1, user2) -> {
return user1.getAge() - user2.getAge();
})
.distinct()
.skip(2)
.forEach(user -> {
System.out.println("user = " + user.toString());
});
}
2.2.7 flatMap
map是操作一个对象,而flatMap可以操作对象集合,先将集合转换为流对象,在对流对象中的对个对象进行操作。
比如我在User中在增加一个属性
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class User {
private String name;
private Integer age;
private String email;
private List<Trait> traitList;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class Trait {
private Integer height;
private Integerweight;
}
初始化值
public static void main(String[] args) {
List<Trait> listTrait = new ArrayList<>();
Trait trait=new Trait(180,60);
Trait trait1=new Trait(181,70);
Trait trait2=new Trait(182,72);
Trait trait3=new Trait(183,73);
Trait trait4=new Trait(184,45);
listTrait.add(trait);
listTrait.add(trait1);
listTrait.add(trait2);
listTrait.add(trait3);
listTrait.add(trait4);
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com",listTrait));
list.add(new User("李四", 22, "123@.com",listTrait));
list.add(new User("王二", 18, "123@.com",listTrait));
list.add(new User("麻子", 45, "123@.com",listTrait));
}
例子,打印所有数据的身高体重,按照身高降序排序
list.stream()
.flatMap(
//将身高体重的集合转为流对象
user->{return user.getTraitList().stream();}
).distinct()
.sorted((trait11,trait22)->{return trait22.getHeight()-trait11.getHeight();})
.forEach(user->{
System.out.println("user = " + user.toString());
});
2.3 终结操作
流操作从创建流开始,中间操作用于对流进行处理,最后就需要终结操作来决定流最后的走向,我可以通过终结操作将流里面的数据打印出来,也可以在将流对象收集为集合,具体怎么做,就取决于我们进行怎样的终结操作。
2.3.1 forEach
对流中的元素进行遍历操作,之前演示的例子一直都是使用forEach,到这里对于forEach 的操作也比较熟悉了
2.3.2 count
获取流中元素的个数
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
long count = list.stream().count();
System.out.println("count = " + count);
}
2.3.3 max和min
获取流中的最大值或者最小值
例如,获取流中年龄最大的元素,并打印。
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
Optional<User> max = list.stream().max((user1, user2) -> {
return user1.getAge() - user2.getAge();
});
System.out.println("max = " + max);
}
或者,获取流中年龄最小的元素,并打印。
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
Optional<User> max = list.stream().max((user1, user2) -> {
return user2.getAge() - user1.getAge();
});
System.out.println("max = " + max);
}
还记得sorted什么情况下是升序,什么情况下是降序吗?
第一个参数减第二个参数,是升序,也是最大值
第二个参数减第一个参数,是降序,也是最小值
2.3.4 collect
把流对象转换为集合。
获取一个存放所有姓名的List集合
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
List<String> collect = list.stream().map((user -> {
return user.getName();
})).collect(Collectors.toList());
//使用Stream流遍历集合
collect.stream().forEach(userName->{
System.out.println("userName = " + userName);
});
获取一个存放所有年龄set集合
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
Set<Integer> collect = list.stream().map((user -> {
return user.getAge();
})).collect(Collectors.toSet());
//使用Stream流遍历集合
collect.stream().forEach(age->{
System.out.println("age = " + age);
});
}
获取一个姓名为Key,年龄为Value的map集合
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
Map<String, Integer> collect = list.stream()
.collect(Collectors.toMap(user -> user.getName(),
user -> user.getAge()));
//使用Stream遍历map
Set<Map.Entry<String, Integer>> entries = collect.entrySet();
entries.stream()
.forEach(map -> System.out.println(map.getKey()+":"+map.getValue()));
}
2.3.5 anyMatch
判断流中是否存在符合条件的元素,如果存在返回true
例如,判断是否存在年龄大于40 的user
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
boolean b = list.stream().anyMatch(user -> user.getAge() > 40);
System.out.println("b = " + b);
}
2.3.6 allMatch
判断流中的元素是否都符合条件,都符合条件返回true,否则为false。
判断流中的元素是否年龄都大于40
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
boolean b = list.stream().allMatch(user -> user.getAge() > 40);
System.out.println("b = " + b);
}
2.3.7 noneMatch
和allMatch相反,判断流中的元素是否都不符合条件,都不符合条件返回true,否则为false。
判断流中的元素是否年龄都大于40
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
boolean b = list.stream().noneMatch(user -> user.getAge() > 40);
System.out.println("b = " + b);
}
2.3.8 findAny
获取流中的任意一个元素
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
list.add(new User("麻子", 45, "123@.com"));
Optional<User> any = list.stream().findAny();
System.out.println(any.toString());
}
2.3.9 findFirst
获取流中的第一个元素
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("麻子", 45, "123@.com"));
list.add(new User("张三", 34, "123@.com"));
list.add(new User("李四", 22, "123@.com"));
list.add(new User("王二", 18, "123@.com"));
Optional<User> findFirst = list.stream().findFirst();
System.out.println(findFirst.toString());
}
三、注意事项
- 惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
- 流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
- 不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的)