这里主要针对Lamda表达式与Stream流做详解,也会牵扯一下其他几个特性
Tips
我大致围绕着lamda、stream、方法引入、Optional用法这几个方面去解析。上半部分我大致说一下Lamda 与 Stream,后续会更新其他。欢迎指正奥!
1. 说说JDK8 的一些新特性、好处
在Jdk 8 中,大致总结以下几点。个人觉得最重要的就是lamda表达式与stream流,其他的也并不是不重要。hh
第一点:在接口中可以定义普通方法
。
第二点:lamda表达式(重点)
。
第三点:就是一个函数式接口
。
第四点:方法与构造函数的应用
。
第五点:Stream流(重点)
接口。
那么他的好处是什么呢?
说专业一点就是简化我们匿名内部类的调用,达到代码简化。如果要做到更精简,就要通过Lamda表达式
+方法引入
从而使代码更加精简
2. Lamda表达式
2.1 Lamda默认方法和普通方法
第一个说到从jdk1.8开始,在接口中支持使用static与default修饰方法体,不需要被重写。下面附上代码!
IDefaultLamda 接口
public interface IDefaultLamda {
void add();
/**
* jdk 8 提供默认的方法,有的可能会存在报错,原因是jkd没有1.8以上
*/
default void defaultGet(){
System.out.println("接口中定义了一个添加的普通方法体");
}
/**
* 静态方法
*/
static void staticDelete(){
System.out.println("接口中定义了一个删除的普通方法体");
}
}
DefaultLamdaImpl实现类
/**
* 实现接口,但这里是没有强制要求去实现默认和静态方法的
*/
public class DefaultLamdaImpl implements IDefaultLamda {
@Override
public void add() {
System.out.println("重写了add方法");
}
}
测试类TestDefault,包含结果
public class TestDefault {
public static void main(String[] args) {
DefaultLamdaImpl defaultLamda = new DefaultLamdaImpl();
defaultLamda.add();
defaultLamda.defaultGet();
IDefaultLamda.staticDelete();
// ===================打印结果=============================
// 重写了add方法
// 接口中定义了一个添加的普通方法体
// 接口中定义了一个删除的普通方法体
}
}
2.2 Lamda表达式的规范(函数接口定义)
使用lamda表达式,他其实使依赖于函数接口。那么我们们说一下函数接口
- 在接口中,只能允许有一个抽象方法。
- 在函数接口中定义Object类中的方法。(也就是父类中的一些方法,可以被重写。toString()、equls方法)
- 使用的是默认或静态方法。
- 注解@FunctionalInterface,标识该接口为函数接口。包括在一些接口(Runnable)都加了@FunctionalInterface这个注解
2.3 Lamda表达式基础语法
语法解释:
()
:参数列表。
->
:分割。
{}
: 方法体。
下面演示一下无参方法的调用,与带参数的方法调用。
函数接口(提供给无参):
@FunctionalInterface
public interface ILamdaTest1 {
void getSize();
}
函数接口(提供给有参):
@FunctionalInterface
public interface ILamdaTest2 {
String getName(int a,int b,int c);
}
无参与有参数的调用:
public static void main(String[] args) {
/* -----------------------无参的写法 START-----------------------*/
// 第一种写法,更为精简
((ILamdaTest1) () -> System.out.println("使用到Lamda表达式的无参表达1")).getSize();
// 第二种写法
ILamdaTest1 lamdaTest =()-> System.out.println("使用到Lamda表达式的无参表达2");
lamdaTest.getSize();
//===========================打印结果==========================
// 使用到Lamda表达式的无参表达1
// 使用到Lamda表达式的无参表达2
/* -----------------------无参的写法 END-----------------------*/
/* -----------------------有参的写法 start-----------------------*/
// 第一种写法更为精简
String name = ((ILamdaTest2) (a, b, c) -> a + "-" + b + "-" + c).getName(2, 3, 4);
System.out.println("有参有返回获取到的值1:"+name);
// 第二种写法
ILamdaTest2 lamdaTest2s = (a, b,c) -> a+"加"+b+"加"+c;
System.out.println("有参有返回获取到的值2:"+lamdaTest2s.getName(5, 10,5));
//===========================打印结果==========================
// 有参有返回获取到的值1:2-3-4
// 有参有返回获取到的值2:5加10加5
/* -----------------------有参的写法 END----------------------- */
}
2.4 使用Lamda表达式来遍历集合
@Test
public void testForeach(){
// 首先创建一个list集合
List<Integer> list = new ArrayList<>();
list.add(12);
list.add(23);
list.add(67);
list.add(78);
list.add(85);
list.add(45);
list.add(46);
list.add(28);
list.add(19);
list.add(99);
System.out.println("当前的list 大小:"+list.size());
// 使用foreach来遍历
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println("使用普通foreach方法:"+integer);
}
});
// 使用lamda
list.forEach(integer -> System.out.println(integer));
}
2.5 使用Lamda表达式来对集合进行排序
首先我们去创建一个实体类 UserEntity:
,自己生成有参构造方法、生成get和set方法、重写toString,然后就进行测试
public class UserEntity {
private String name;
private Integer age;
}
测试类SortingTest:
public class SortingTest {
public static void main(String[] args) {
List<UserEntity> list = new ArrayList<>();
list.add(new UserEntity("zhangsan1",54));
list.add(new UserEntity("zhangsan2",25));
list.add(new UserEntity("zhangsan3",18));
list.add(new UserEntity("zhangsan4",19));
list.add(new UserEntity("zhangsan5",35));
list.add(new UserEntity("zhangsan6",59));
/* 第一种演示*/
/*list.sort(new Comparator<UserEntity>() {
@Override
public int compare(UserEntity o1, UserEntity o2) {
return o1.getAge() - o2.getAge();
}
});*/
/* 第二种演示*/
// list.sort((o1, o2) -> o1.getAge() - o2.getAge());
/* 第三种演示 */
list.sort(Comparator.comparingInt(UserEntity::getAge));
// 再次遍历即可
list.forEach(d -> System.out.println(d));
}
}
运行结果:
UserEntity{name='zhangsan3', age=18}
UserEntity{name='zhangsan4', age=19}
UserEntity{name='zhangsan2', age=25}
UserEntity{name='zhangsan5', age=35}
UserEntity{name='zhangsan1', age=54}
UserEntity{name='zhangsan6', age=59}
3 Stream 流
Stream流,它可以用非常方便且精简的形式去遍历、过滤、排序、分页、去重等操作。我们可以知道创建流的两种(串行流stream()
、并行流parallelStream()
)方式。
串行流并行流的区别?
串行流我们可以理解为单线程,单个线程去处理。并行流也就可以理解为多个线程去处理。因此是可以知道,并行流的效率高于串行流。
Stream流的一个流程?
1.集合
----------》2.创建Stream 流
----------》3.中间操作(filter、sorted、limit等)
----------》4.终止操作(foreach(遍历)、collect(转换)、min、max、allMatch(条件))
下面我分别截出了使用lamda和不使用lamda的写法图片,截出来是为了对于不太会lamda表达式写法的同胞们,可以使用匿名内部类写法,然后通过idea转成lamda~~~~,注意截图中有文字说明噢。
3.1 用Stream将List 转化成Set
首先还是创建一个实体类StreamEntity,这里你们自己重写tostring、构造、以及生成get和set方法,然后进行测试
public class StreamEntity {
private String name;
private Integer age;
}
public static void main(String[] args) {
List<StreamEntity> list = new ArrayList<>();
list.add(new StreamEntity("zhangsan1",12));
list.add(new StreamEntity("zhangsan2",234));
list.add(new StreamEntity("zhangsan3",54));
list.add(new StreamEntity("zhangsan4",23));
list.add(new StreamEntity("zhangsan5",8));
list.add(new StreamEntity("zhangsan6",39));
list.add(new StreamEntity("zhangsan7",29));
list.add(new StreamEntity("zhangsan7",29));
// 将他转换成set集合,请注意,这里不会去重,因为存对象地址值不一样
Set<StreamEntity> collect = list.stream().collect(Collectors.toSet());
collect.forEach(data-> System.out.println(data));
}
3.2 Set去重以及底层实现原理
Set集合底层是依赖于map集合实现防重复key的,map集合底层基于equals比较和hashcode实现防重复的
首先我们来做个比较,new 出两个对象,相同的值,不去重写equals的话,就会返回false
@Test
public void setDuplicateRemovalOne(){
StreamEntity streamEntity1 = new StreamEntity("zhangsan7", 29);
StreamEntity streamEntity2 = new StreamEntity("zhangsan7", 29);
/* 这里我已经重写了equals 方法,我们比较的是他们的内容值是否相等。如果没有重写equals 方法的话,则返回false*/
System.out.println(streamEntity1.equals(streamEntity2)); //打印结果为 true
}
在SreamEntity
中重写equals
方法,这里我们比较的是他们的内容,所以,不管你怎么new,只要内容一样,就返回true
/**
* 重写object方法
*/
public boolean equals(Object obj){
if (obj instanceof StreamEntity){
return name.equals(((StreamEntity)obj).name) && age == (((StreamEntity)obj).age);
}else {
return false;
}
}
如果是一个map集合切有相同的key,但value不同呢?这里我们就要去重写hashCode了。
在SreamEntity
中重写hashCode
方法
/**
* 重写hashCode
* @return
*/
public int hashCode(){
return name.hashCode();
}
@Test
public void setDuplicateRemovalTwo(){
StreamEntity streamEntity1 = new StreamEntity("zhangsan7", 29);
StreamEntity streamEntity2 = new StreamEntity("zhangsan7", 29);
//重写hashcode 去重后
HashMap<Object, Object> objectHashMap = new HashMap<>();
objectHashMap.put(streamEntity1,"one");
objectHashMap.put(streamEntity2,"two");
objectHashMap.forEach((k,v)-> System.out.println(k+","+v));
}
打印结果:
3.3 利用Stream将List转换成Map
这里先一个讲解,没有用lamda表达式的形式,但在开发中,是肯定要转成lamda表达式的。暂时不贴代码,多敲。。。
未用lamda:
使用lamda:
最终结果:
3.4 利用Stream做求和(reduce)
未用lamda:
使用lamda:
最终结果:
3.5 利用Stream求最大值(max)与最小值(min)
内容一样,把max 换称min就是求最小值
未用lamda:
使用lamda:
这里替换成了Comparator
最终结果:
3.6 Stream 的Match用法(anyMatch 和 allMatch)
它是用来做匹配的,有匹配上的就返回true,没有则返回fasle。
未用lamda:
使用lamda:
最终结果:
3.7 Stream 的过滤器(filter)
未用lamda:
使用lamda:
最终结果:
3.8 Stream实现分页(limit)
3.9 Stream 对集合进行排序(sorted)
未用lamda:
使用lamda:
最终结果: