Java8在2014年3月發佈,但是作爲IT公司往往不是追求技術的新,而且追求技術的穩定。所以大多用的之前的版本。
Java 8 新特性簡介
- 速度更快(1、修改底層數據結構:如HashMap(數組-鏈表-紅黑樹),HashSet,ConcurrentHashMap(CAS算法)
2、修改垃圾回收機制:取消堆中的永久區(PremGen)->回收條件苛刻,使用元空間(MetaSpace)->直接使用物理內存->加載類文件) - 代碼更少(增加了新的語法Lambda表達式)
- 強大的Stream API
- 便於並行
- 最大化減少空指針異常 Optional容器類
下面是個人對Lambda表達式的一些認識:
1、lambda表達式最大的作用就是簡化了我們的代碼編寫,使我們的代碼更加的優雅。但是他並不能對於程序效率提升多少。
2、我個人感覺lambda表達式像是對內部類做了簡化,在需要使用內部類的地方,不需要再去new
一個內部類了,僅僅只需要寫內部類中最核心的代碼就可以了。像是一個匿名方法一樣,只用寫參數和方法實現!
代碼示例
**需求:**現在有一個List集合
List<employee> emps
(employee
代表員工),現在要求篩選出該集合中的員工年齡大於40歲的所有員工!
Employee.java
public class Employee {
private int id;
private String name;
private int age;
private double salary;
}
我們首先想到的方法可能是,寫一個方法,直接將 emps
集合進行遍歷,然後判斷年齡是否大於40歲,如果是則加入新的list集合中,遍歷完成以後就將新的list集合返回即可。如下:
public List<Employee> filterEmployeeAge(List<Employee> emps){
List<Employee> list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getAge() <= 35){
list.add(emp);
}
}
return list;
}
這樣做似乎沒什麼問題,但是現在又有一個新的需求了,現在需要過濾出薪水 salary
大於3000的所有員工,於是乎我們又需要編寫一個通過員工薪水過濾的方法!如下:
public List<Employee> filterEmployeeSalary(List<Employee> emps){
List<Employee> list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getSalary() >= 5000){
list.add(emp);
}
}
return list;
}
這樣做也沒什麼問題,但是現在又有一個新的需求了,現在需要過濾出員工id大於500的所有員工,於是我們又需要編寫一個通過員工id過濾的方法!如下:
public List<Employee> filterEmployeeId(List<Employee> emps){
List<Employee> list = new ArrayList<>();
for (Employee emp : emps) {
if(emp.getId() >= 5000){
list.add(emp);
}
}
return list;
}
1、寫着寫着我們就開始發現問題了,所編寫的幾個方法中代碼邏輯都一樣,只有一行判斷條件不同。我們寫了很多的冗餘代碼。
2、我們可能還會通過員工姓名、生日、愛好、家庭地址、電話號碼等等信息來過濾員工。那麼我們就需要編寫一個又一個的重複方法。這樣就使得我們的代碼變得冗餘極其不優雅。所以我們需要進行改進。
改進方案
方案1(策略模式)
通過策略設計模式來對我們的代碼進行改進,首先我們定義一個過濾的方法,你需要通過什麼條件進行過濾員工,那麼將你的過濾策略通過方法的形參傳入進來即可。在方法的內部會自動的根據你傳入的策略進行過濾員工。
通過策略模式,我們知道我們首先需要去定義一個策略接口,每一個實現了該接口的類都是一個策略。如下:
@FunctionalInterface
public interface MyPredicate<T> {
public boolean test(T t);
}
現在我們要通過員工的id來過濾員工,過濾出員工id大於500的所有員工,所以我們要去定義一個策略實現類,繼承自定義的接口MyPredicate。在實現類中覆寫test方法去指定具體過濾策略。如下:
public class FilterEmployeeForId implements MyPredicate<Employee>{
//通過員工id過濾員工策略
@Override
public boolean test(Employee t) {
return t.id() > 500;
}
}
然後在編寫一個過濾方法,通過傳入的過濾策略來過濾員工。如下:
public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
List<Employee> list = new ArrayList<>();
for (Employee employee : emps) {
if(mp.test(employee)){
list.add(employee);
}
}
return list;
}
在我們調用該方法來按條件過濾員工時,我們只需要傳入實現了策略接口的具體實現類即可。如下:
@Test
public void test(){
List<Employee> list = filterEmployee(emps, new FilterEmployeeForId());
for (Employee employee : list) {
System.out.println(employee);
}
}
如果,現在又需要通過年齡過濾,過濾出年齡小於40歲的員工。那麼我們只需要再定義一個具體策略類去實現策略接口MyPredicate即可。然後在調用的時候傳入該策略實現類即可。不用再去寫一個for循環遍歷emps集合然後通過年齡做判斷了。
但是如果這樣做的話,每當我們要增加一個過濾需求,我們都需要自己去定義一個策略實現類然後實現策略接口,這樣也顯得比較麻煩!所以還需要進行改進!
方案2(匿名內部類)
我們仍然需要編寫一個策略接口,但是當我們要通過具體xxx條件去過濾員工的時候,我們不用再自己去定義一個類然後實現他的策略方法了。我們可以直接通過匿名內部類完成。在匿名內部類中實現具體的策略!比如現在要過濾出員工id小於500的員工。如下:
@Test
public void test5(){
List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
//匿名內部類中實現具體的策略
@Override
public boolean test(Employee t) {
return t.getId() <= 500;
}
});
for (Employee employee : list) {
System.out.println(employee);
}
}
如果我們現在又要過濾出員工年齡小於40的所有員工,那麼只需要改動匿名內部類中的過濾條件即可。但是這樣的話,每一個不同的過濾條件僅僅是匿名內部類中的判斷條件不同,其他的部分還是都是一樣的(比如new MyPredicate() { xxx }),這樣代碼仍然不優雅,我們還需要做一定的修改。所以就引出了lambda表達式。
方案3(lambda表達式)
從方案2中我們看到了,每一個不同的過濾需求,其實只有匿名內部類中的一小部分改動,其他都是一樣的。但是我們每次都要去手動創建匿名內部類。如果我們每次能只寫過濾的核心條件就可完成員工的過濾多好呀!lambda表達式爲我們做到了這一點。繼方案2,使用lambda表達式,我們只需要傳入核心的判斷條件即可。如下:
public void test(){
List<Employee> list = filterEmployee(emps, (e) -> e.getId() <= 500);
list.forEach(System.out::println);
}
**說明:**在上面的代碼中我們看到了,lambda表達式僅僅通過一行代碼((e) -> e.getId() <= 500
)就表達了核心過濾條件。不用再去寫匿名的內部策略類了。這裏lambda表達式其實是對接口中的 test(xxx)
方法的一個實現。左邊是該方法需要的參數,右邊是該方法的方法體的具體內容。從上面的方案2和方案3可以看出,lambda表達式其實就是對使用匿名內部類的地方做了一個簡化,我們只需要去寫這個匿名內部類中的方法的具體邏輯即可,不用再去new 一個匿名內部類了,大大的簡化了代碼編寫
總結
- lambda表達式可以簡化某些匿名內部類的寫法
- lambda表達式是匿名內部類的語法糖
- 使用lambda表達式可以使我們的代碼更加的簡潔優雅
- lambda表達式可以使代碼變得簡潔,如果某些地方強行使用這也可能會造成某些代碼的可讀性降低
- lambda表達式並不會對性能有多大的提升