03.29 Java8解決了什麼?

Java8解決了什麼?

在學習面向對象時,許多人都會用Java來舉例子,但是其實Java並非純正的面嚮對象語言,最明顯的就是:int,double等基本類型不是對象。

自從java8出來過後,引入了流,函數式編程,就更不是在向著面向對象發展了。有人可能會感到詫異,為啥越來越偏離我們遵循了這麼久的面向對象設計模式?

其實很簡單,我們對工具的改造的最終目的都是為了解決問題,以前有面向過程解決不了的問題,那麼面向對象出來解決了;現在面向對象有許多問題,那麼就可以用函數式編程來解決,所以這些變化是很自然的,Java要在不同時代的保持自己的活力,就必須與時俱進,所以Java8的出現就是自然而然的。

下面我就來探索一下,Java8到底解決了一些什麼問題。

消除冗餘類代碼

假設有個類:

class People{
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {

return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

現在有個list

 List<people> workers = new LinkedList<>();
workers.add( new People("aa",23));
workers.add( new People("abc",21));
workers.add( new People("cdf",18));
/<people>

如果要對這個list按照People的年齡排序,並打印出來,那麼在Java8之前會這樣寫:

workers.sort(new Comparator<people>() {
@Override
public int compare(People o1, People o2) {
return o1.getAge()>o2.getAge()?1:-1;
}
});
for (People p:workers ) {
System.out.println(p.getName()+":"+p.getAge());
}
/<people>

Java8引入了函數式編程的lambda表達式,就可以這樣寫了:

workers.sort((o1, o2) -> o1.getAge()>o2.getAge()?1:-1);
for (People p:workers ) {
System.out.println(p.getName()+":"+p.getAge());
}

進一步,使用方法引用就可以這樣寫:

workers.sort(Comparator.comparing(People::getAge));
for (People p:workers ) {
System.out.println(p.getName()+":"+p.getAge());
}

這樣就更簡潔了,而且意圖也和清晰,可以不寫註釋就能讓別人明白,是按照年齡排序。

函數式的lambda表達式通過將函數提升為“一等公民”,使得直接傳遞函數成為可能,而不必再為了傳遞實現某個功能的函數而強行傳遞一個冗餘的外包類。

內部迭代替代外部迭代

stream允許你以聲明性方式處理數據集合,類似於SQL語句。我們直接看例子吧,上面那一段代碼已經很簡潔了,但是使用了流還可以更簡潔

 workers.sort(Comparator.comparing(People::getAge));
workers.stream().map(p-> p.getName()+":"+p.getAge()).forEach(System.out::println);

這一段代碼根本就沒有循環了,Stream API 替你搞定了循環,這就是內部迭代 替代 外部迭代 ,亦即API的設計者使用者完成了迭代,代碼相當

簡潔

而且由於是內部迭代,所以Stream庫可以選擇最適合本機硬件的實現,達到性能優化的目的,如果是外部迭代,就需要調用者自己來優化了(你得承認許多API調用者沒有這種優化能力)。

安全簡潔地並行

如果我們有大量數據要處理,通常會使用多線程,在Java8之前,使用多線程是一件比較麻煩的事:

  • 我們得自己合理的劃分數據
  • 手動為每一部分數據單獨分配一個線程,還有可能會產生競態條件需要進行同步
  • 完成每個線程的結果的合併,得到最終結果。

這個過程是比較麻煩的,易錯的。使用流能夠安全簡潔的使用多核,甚至於你都不需要關心多線程的具體實現。

傳統的循環:

List<string> intList = new LinkedList();
for (int i=0;i<20_000_000;i++){

intList.add(i+"");
}
long count = 0;
long start = System.nanoTime();
for (String in:intList ) {
if (Integer.parseInt(in)>1_123_345) count++;
}
long duration = (System.nanoTime() - start) / 1_000_000;
System.out.println(duration);
/<string>

結果:781,747,770

Java8的並行流:

List<string> intList = new LinkedList();
for (int i=0;i<20_000_000;i++){
intList.add(i+"");
}
long count = 0;
long start = System.nanoTime();
count = intList.parallelStream().filter(p->Integer.parseInt(p)>1_123_345).count();
long duration = (System.nanoTime() - start) / 1_000_000;
System.out.println(duration);
/<string>

結果:665,683,662

可見我們並不需要顯式的進行多線程編程就能取得比單線程更好的結果,也就是Stream庫幫我們實現了多線程,更安全(不得不承認許多人寫的多線程代碼都是有問題的),更簡潔(我們不用寫多線程代碼了)。

稍微需要注意的是,多線程本身會帶來一定開銷,所以如果問題規模不夠大的話(具體數值取決於你的硬件),單線程反而優於多線程,所以使用之前要先考慮和測試,多線程到底能否帶來好處。一個簡單的原則是:要處理的問題規模很龐大,或處理單個問題特別耗時就可以考慮多線程了。


總之,Java8還有好多新特性,會幫我們解決許多以前無法解決的問題,所以我們要與時俱進,好好學習啊(~_~)。


分享到:


相關文章: