在jdk1.8版本之前,我们操作一些数据,会让人感觉很繁琐,我们明明只要判断一个内容,如果存在,则输出,这明显可以一句代码完成啊,可是,在java1.8版本之前,我们必须使用一个循环来搞定,自从jdk1.8面世以来,给我们带来了好多特性,但是好多人还沉浸在jdk1.6+中,emmm,时代在发展,也不能停止学习。
一、lambda表达式
什么是lambad?lambad就是允许把函数作为一个方法的参数。
其中lambad就是允许把函数作为一个方法的参数,这句话怎么理解呢?学过javaScript的同学一定不会陌生,因为我们可以直接在函数中传递函数。
eg:
function test(callback){
console.log('hello javaScript')
callback()
}
function run(){
test(()=>{
console.log('我只是callback的内容')
})
}
run()
1.1 labmda操作循环
我们可以清晰的看出来,在javaScript语言我们可以足够将函数作为参数传入方法。在jdk1.8之前,java开发者根本不可能达到类似的效果。
在java1.8之前,我们想要输出一个列表的内容,我们可以使用for循环:
List<String> list = new ArrayList();
list.add("raven");
list.add("hello world");
list.add("123");
for (String s:list){
System.out.println(s);
}
而升级到jdk1.8之后,我们可以直接使用lambda表达式,如下:
List<String> list = new ArrayList();
list.add("raven");
list.add("hello world");
list.add("123");
//可选的类型声明
list.forEach((e)-> {
System.out.println(e)
});
//可选的大括号
list.forEach((String e)-> System.out.println(e));
//可选的小括号
list.forEach(e->{
System.out.println(e)
});
其中需注意:
- 可选类型声明:也就是说上面的对象e不用你声明,编译器可以识别
- 可选的参数圆括号:也就是当lambda表达式只有一个参数的时候,括号可要可不要,如果有多个参数的话,那么必须加括号包住参数
- 可选的大括号:如果lambda表达式里面只有一句代码,那么不必要加外面的大括号
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
1.1 labmda创建线程
之前我们创建线程可以如下:
// 普通方法创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("普通的创建线程方法");
}
}).start();
// 匿名类创建线程
new Thread(){
@Override
public void run() {
System.out.println("匿名类创建线程");
}
}.start();
而使用了lambda创建线程,就更简洁了:如下:
new Thread(()->{
System.out.println("我是lambda方法创建的线程");
}).start();
那么为什么我们可以这么做呢?因为Runnable
接口中,如下图:
首先在Thread方法中,有唯一一个含参(Runnable)的方法,而Runnable只有 一个公共无参方法,所以编译器当然可以自动识别到你写的lambda方法所对应要适配方法是哪个方法。
所以我们平时调用一个方法的时候,如果该参数是一个接口类型的参数,那么我们就可以使用lambda表达式去简化开发。
二、stream流式特性
2.1 filter(过滤)
这种过滤不要太简单了。像之前的话还得写个for循环判断才能拿到数据,而使用Stream特性一句代码搞定!
List<String> list = new ArrayList();
list.add("raven");
list.add("hello world");
list.add("hello java");
list.add("hello python");
List<String> collect = list.stream().filter(e -> e.contains("hello")).collect(Collectors.toList());
2.2 sorted(排序)
有时候我们还需要list排序。
List<String> list = new ArrayList();
list.add("4");
list.add("1");
list.add("3");
list.add("2");
List<String> sorted = list.stream().
sorted().collect(Collectors.toList());
//按自然输入排序后 输出 [1, 2, 3, 4]
System.out.println(sorted);
当然这只是String对象,如果是其他对象该怎么按照自定义属性怎么排序呢?
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
list.add(new User(6));
list.add(new User(6));
// 方法1 自定义比较器
List<User> collect = list.stream().sorted(
(o1, o2) -> o1.getId()-o2.getId() )
.collect(Collectors.toList()
);
collect.forEach(e-> System.out.println(e));
// 方法2 推荐使用comparing 方法
List<User> collect2 = list.stream().sorted(Comparator.comparing(User::getId)).collect(Collectors.toList());
collect2.forEach(e-> System.out.println(e));
// 逆序
List<User> collect3 = list.stream().sorted(Comparator.comparing(User::getId).reversed()).collect(Collectors.toList());
collect3.forEach(e-> System.out.println(e));
如上代码就可以让List的数据按照我们自定义的id属性进行排序啦,当然我们也可以直接使用List自带的sort方法排序。如:
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
//方法1
list.sort((o1, o2) -> o1.getId()-o2.getId() );
list.forEach(e-> System.out.println(e));
//方法2 推荐使用Comparator.comparing方法
list.sort(Comparator.comparing(User::getId));
list.forEach(e-> System.out.println(e));
另外如果我们的User对象如果实现了比较器,直接用即可:
public class User implements Comparable{
private int id;
public User(int id) {
this.id = id;
}
@Override
public int compareTo(Object o) {
if(this.getId() > ((User)o).getId()){
return 1;
}else if(this.getId() == ((User)o).getId()){
return 0;
}else{
return -1;
}
}
}
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
list.add(new User(6));
list.add(new User(6));
List<User> sorted = list.stream().sorted().collect(Collectors.toList());;
sorted.forEach(e-> System.out.println(e));
2.3 limit
返回前n个元素的流,下面这段代码,返回查询到的数据的前两个
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
list.add(new User(6));
list.add(new User(6));
List<User> collect = list.stream().filter(e -> e.getId() > 10).limit(2).collect(Collectors.toList());
collect.forEach(e-> System.out.println(e));
// 输出
//User{id=227}
//User{id=12}
2.3 skip
跳过n个元素后输出
首先过滤id>6的的数据,再从过滤后的数据的第二个数据开始输出
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
list.add(new User(6));
list.add(new User(6));
List<User> collect = list.stream().filter(e -> e.getId() > 5).skip(2).collect(Collectors.toList());
collect.forEach(e-> System.out.println(e));
// 输出
//User{id=12}
//User{id=227}
2.4 map
可将查到的列表里提取其中的一个属性作为新的列表 ,以下我提取了user的id属性。
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
List<Integer> collect = list.stream().map(User::getId).collect(Collectors.toList());
collect.forEach(e-> System.out.println(e));
2.5 flatMap
flatMap与map的区别在于 flatMap是将一个流中的每个值都转成一个个流。
List<List<String>> type = new ArrayList<>();
List<String> person = new ArrayList<>();
person.add("高智商");
person.add("会说话");
List<String> dog = new ArrayList<>();
dog.add("低智商");
dog.add("会汪汪汪");
type.add(person);
type.add(dog);
//flatMap(Collection::stream)
List<String> collect = type.stream().flatMap(e->{
String s = String.join(",",e);
return Stream.of(s);
}).collect(Collectors.toList());
//输出
//高智商,会说话
//低智商,会汪汪汪
collect.forEach(e-> System.out.println(e));
下面的代码先通过map把数组转换成了字符串数组的数组,如
[ [“H”,“e”,“l”,“l”,“o”],[“W”,“o”,“r”,“l”,“d”] ],然后再用flatMap方法把大数组里面的两个数组都转换成流处理,得到[H, e, l, l, o, W, o, r, l, d] ,然后去重,最后转变成数组。
String[] strings = {"Hello", "World"};
List collect = Arrays.asList(strings).stream().
map(s -> s.split("")).flatMap(Arrays::stream)
.distinct().collect(Collectors.toList());
System.out.println(collect);
//输出 [H, e, l, o, W, r, d]
2.6 allMatch
检测里面的数据是否都满足条件.
显而易见,所有的id都>=10,应该输出true
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
boolean flag = list.stream().allMatch(e -> e.getId() >= 10);
System.out.println(flag);
2.7 anyMatch
若存在满足条件的则就为true
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
boolean flag = list.stream().anyMatch(e -> e.getId()>200);
System.out.println(flag);
2.8 noneMatch
判断查询结果是否为空,如果为空,则为true
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
boolean flag = list.stream().noneMatch(e -> e.getId()>200);
System.out.println(flag);
2.9 reduce
可以用来对数据的一些操作。
List<User> list = new ArrayList();
list.add(new User(227));
list.add(new User(10));
list.add(new User(12));
list.add(new User(127));
// 求id总和
list.stream().map(User::getId).reduce((i,j)-> i+j).ifPresent(System.out::println);
//id最大值
list.stream().map(User::getId).reduce(Integer::max).ifPresent(System.out::println);
//id最小值
list.stream().map(User::getId).reduce(Integer::min).ifPresent(System.out::println);
2.99 collect
List<User> list = new ArrayList();
list.add(new User(227,"谢霆锋"));
list.add(new User(10,"陈冠希"));
list.add(new User(12,"余文乐"));
list.add(new User(127,"吴彦祖"));
// 输出总个数、总和、最小值、平均值、最大值
IntSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingInt(User::getId));
System.out.println(summaryStatistics);
String str = list.stream().map(u -> u.getUserName()).collect(Collectors.joining("的朋友"));
// 用‘的朋友’连接所有名字 故输出 谢霆锋的朋友陈冠希的朋友余文乐的朋友吴彦祖的朋友陈冠希
System.out.println(str);
// 用姓名做key,值为姓名分组的列表
Map<String, List<User>> collect = list.stream().collect(Collectors.groupingBy(User::getUserName));
System.out.println(collect);
// 将陈冠希分一组,其他的分一组
Map<Boolean, List<User>> collect2 = list.stream().collect(Collectors.partitioningBy(u->u.getUserName().equals("陈冠希")));
System.out.println(collect2);
总结
关于java1.8 的特性还是挺多的,虽然不能面面俱到,但是每次回首也固然有收获。