歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java8中Lambda表達式應用及泛型相關

Java8中Lambda表達式應用及泛型相關

日期:2017/3/1 9:07:02   编辑:Linux編程

Java8中Lambda表達式語法部分就不寫了,我們直接拋出一個實際問題,看看Iava8的這些新特性究竟能給我們帶來哪些便利。

順帶用到一些泛型編程,一切都是為了簡化代碼

場景:

一個數據類,用於記錄職工信息

public class Employee {

public String name;
public int age;
public char sex;
public String time;
public int salary;

}

我們有一列此類數據

List<Employee> data = Arrays.asList(e1,e2,e3......)

現在有需求:將員工Employee按照姓名的首字母(假設均為英文名)進行分組:

那麼我們要得到的結果應該是一個Map:char -> List<Employee> 這樣的映射關系

public static Map<Character, List<Employee>> groupByFirstChar(

List<Employee> data){

Map<Character, List<Employee>> result = new HashMap<>();

for(Employee e : data){

Character c = e.name.charAt(0);

List<Employee> l = result.get(c);

if(l == null){

l = new ArrayList<>();

result.put(c, l);

}

l.add(e);

}

return result;

}

代碼並不復雜,很快就可以完成,老板看你效率這麼高,於是說,再按照工資分個組吧,5000以下的,5000~10000的 ...等

也不會太難,將key換一下,稍作邏輯處理即可

public static Map<String, List<Employee>> groupBySalary(List<Employee> data) {

Map<String, List<Employee>> result = new HashMap<>();

for (Employee e : data) {

String key = separate(e.salary);

List<Employee> l = result.get(key);

if (l == null) {

l = new ArrayList<>();

result.put(key, l);

}

l.add(e);

}

return result;<br>}

private static String separate(int salary) {

if (salary <= 5000) {

return "5000以下";

}

if (salary <= 10000) {

return "5000~10000";

}

if (salary <= 20000) {

return "10000~20000";

}

return "20000以上"

}

然後老板又說了,按照員工的入職年份分下組吧。。。

這裡就代碼就不寫了,稍作比較可以發現,無論怎麼分組,唯一的變化是key值的選取方式,

第一次將Employee的name的第一字母作為key:

Employee e -> e.name.charAt(0)

第二次將Employee的salary按照方法separat轉換為String作為key:

Employee e -> separate(e.salary):String

以此類推

Employee e -> getYear(e.time):String

事實上第一次也可以將獲取首字母單獨寫成一個方法

Employee e -> getFirstChar(e.name):Character

為了看起來更美觀,可以講三個方法的參數都設置成Employee 方法體就不寫了 這裡只列出參數和返回值

Employee e -> getFirstChar(e) : Character

Employee e -> separate(e) : String

Employee e -> getYear(e) : String


->的左邊為參數,:的右邊為返回值,->的右邊是方法的簽名

那麼我們自然會想到將變化的部分抽取為參數,其他不變的部分抽取為方法體,那麼就可以省去那些重復的代碼,顯然變化的部分就是上面列出的,將Employee e轉化成key的方法,但是我們知道java是不能把方法作為參數進行傳遞的。不過對於稍有經驗的程序猿來說這並不是問題,我們可以使用接口來實現我們的目的,同時又會遇到另一個問題,以上三個方法的返回值是不同的,因此我們要用到泛型:

public static <K> Map<K, List<Employee>> groupByKey(List<Employee> data, GetKey<K> getKey){

Map<K, List<Employee>> result = new HashMap<>();

for(Employee e : data) {

K key = getKey.getKey(e);

List<Employee> l = result.get(key);

if (l == null) {

l = new ArrayList<>();

result.put(key, l);

}

l.add(e);

}

return result;

}

interface GetKey<K>{
K getKey(Employee e);
}

那麼上面的第一個需求就可以這樣實現

Map<Character, List<Employee>> result = groupByKey(data, new GetKey<Character>() {

@Override

public Character getKey(Employee e) {

e.name.charAt(0);

}

});

第二個需求

Map<String, List<Employee>> result = groupByKey(list, new GetKey<String>() {

@Override

public String getKey(Employee e) {

separate(e.salary);

}

});

可以發現,我們只需要更改泛型參數和匿名內部類的實現即可,唯一的問題恐怕是這麼寫實在不太好看,而且很多例行公事的代碼,尤其體現在匿名內部類上。

事實上我們只關心這個匿名內部類的參數和返回值,其他部分僅僅是語法要求。

java8恰好為我們提供了很好的方式來避免復雜的例行公事的方式:lambda表達式,以上實現就可以寫成

Map<Character, List<Employee>> resultByFirstChar = groupByKey(list, e -> e.name.charAt(0));

Map<String, List<Employee>> resultBySalary = groupByKey(list, e -> separate(e.salary));

lambda表達式恰恰只表現出我們所關心的,參數和返回值,同時由於類型推斷,可以省去參數類型,具體語法這裡就不介紹了,網上可以查到很多資料

extra:

如果你對泛型有不錯的了解的話,方法groupByKey還可以進一步抽象:

public static <K, E> Map<K, List<E>> groupBy(List<? extends E> data, Function<? super E, ? extends K> fun) {

Map<K, List<E>> result = new HashMap<>();

for(E e : data) {

K k = fun.apply(e);<br> List<E> l = result.get(k);

if(l == null) {

l = new ArrayList<>();

result.put(k, l);

}

l.add(e);

}

return result;<br>}

我們將Employee這個類也抽取了,好處顯而易見

Function接口是java8新加入的接口:

@FunctionalInterface

public interface Function<T, R> {

R apply(T t);

}

輸入一個T類型 返回R類型。泛型和函數式編程結合的很不錯,雖然java8的新特性被各種吐槽,但是能帶來好處總是好的,這給了我們更多的選擇。

有時間的話會介紹stream,java8的另一大利器

Copyright © Linux教程網 All Rights Reserved