1. 程式人生 > >用不用lambda,這是一個問題

用不用lambda,這是一個問題

lec main 遠的 理解 array 而且 列表 lam math

什麽是lambda表達式?

眾所周知,Java是一門強大的面向對象的語言,在Java中,除了8種基本的數據類型,其他一切皆為對象, 而數據和對數據的操作,是依賴於對象的屬性和方法。面向對象的三大核心:封裝、繼承、多態都是對數據的抽象,而lambda提供了一種對行為抽象的編程模型。

Java中將方法作為參數進行傳遞的方式被稱為lambda表達式。

lambda表達式是Java對於函數式編程的溫和轉變,面向對象編程和函數式編程不是互相對立的,結合使用能夠更加有效地幫助我們管理程序的復雜性。

為什麽需要lambada?

我認為有以下兩點:將外部叠代轉換為內部叠代使得效率更高、通過將函數作為參數使得編碼更加優雅,更易讀。

隨著摩爾定律的失效,個人電腦與專業服務器中開始配置多核處理器,為了使得程序的運行效率更高,開發者需要能夠將任務分發到多個核心去執行,而Java Collection Framework默認的外部叠代方式將程序綁定在單個核心上運行,極大地降低了運算速度。

有時候為了達到某一目的,我們需要重復很多樣板代碼,這些代碼會侵入業務邏輯,降低了可讀性。

一個例子

假設現在有這麽一個例子:

一個列表中擁有多個元素,元素類型為Integer,現在我要將其中每個元素通過轉換為Point(java.awt.Point),最後得出所有Point中距離原點最遠的那個。

傳統的寫法是:

public static void main(String[] args)
{
List<Integer> intList=Arrays.asList(21,82,33,54);
List<Point>pointList=new ArrayList<>();
for(Integer i:intList)
{
pointList.add(new Point(i%3,i));
// 轉換一
}
double max=Double.MIN_VALUE;
for(Point p:pointList)
{
max =Math.max(p.distance(0,0),max);
// 轉換二
}
}

這樣寫似乎沒有什麽不對,是的,它非常正確。但是依然存在著一些問題。

首先,我們考慮一下這兩個轉換之間的關系。點Integer類型的A需要通過轉換一變為Point類型,再由轉換二將其變為Double類型,這沒有問題。但是代碼中的邏輯是當轉換一全部完成後,再開始轉換二。這似乎沒有必要,並且程序將任務綁定在了一個核心上。假如轉換任務是一系列復雜的計算,那麽串行會導致效率低下。

此外,我們的目的是intList -> Double(距離最大值),但是為了這一目的,在堆上為中間載體pointList分配了空間,假如這個List非常大,無疑會增加初始化以及GC消耗。而且,這段代碼顯得有點臃腫。

匿名內部類 和 lambda

我們首先使用匿名內部類對其進行改造,內部類是一種語法糖,修改之後雖然比之前簡短了一些,但是仍然十分醜陋冗長,並且難以快速理解。


List<Integer>intList =Arrays.asList(21,82,33,54);
final double[] max = {Double.MIN_VALUE};
intList.forEach(new Consumer<Integer>(){
@Override
public void accept(Integer i){
max[0]=Math.max(new Point(i %3, i).distance(0,0), max[0]);
}
});
那麽,我們應該如何對其進行改造呢?首先我們來看看哪些東西其實是不需要的。因為List的泛型是Integer,所以編譯器可以從上下文推斷出來intList.forEach所要操作的元素類型是Integer,其次Consumer是一個interface,準確地來說他是一個函數式接口,我們重寫了它的accept方法,它只是用來接收元素,所以我們可以摒棄這種啰嗦的寫法,利用編譯器自身根據上下文的類型推斷,來書寫更為精簡的lambda表達式:
List<Integer>intList=Arrays.asList(21,82,33,54);
final double[]max={Double.MIN_VALUE};
intList.forEach(i->{ max[0]=Math.max(new Point(i%3,i).distance(0,0), max[0]);

Stream

Stream是JDK1.8中新增的重要API,它可以幫助開發者操作對集合進行復雜的操作,並且能夠自動依賴Fork/Join框架來實現自動並行化。

在傳統方式中,我們可以將數據想象成一種“小球”,而集合則是存放它的“瓶子”。那麽上面的例子應該是這樣:

技術分享圖片

如果我們使用流呢?流提供了一個可選的有序值序列,而且無需為這些值提供任何存儲。我們將其想象成一條河道,其中是流動的數據我們可以在河道上設立“濾網”,通過這些濾網在數據“流動”的時候就對其進行轉化

技術分享圖片

此時轉化一/二都是對流進行操作,簡潔了許多,代碼如下:

List<Integer>intList =Arrays.asList(21,82,33,54);
OptionalDouble max=intList.stream()
.map(i->new Point(i%3,i)) //操作一
.mapToDouble(p->p.distance(0,0)) //操作二
.max();

將上述代碼中的 stream換成 parallelStream,便可將intList中的元素轉換為並行流再進行操作。

總結:
Java中的lambda表達式是對函數式編程的支持,在處理集合等問題上有諸多優勢,能可見地提高代碼質量,理想並且簡潔地解決並發問題
但並不代表函數式編程(FP)優於面向對象編程(OOP)。
隨發展,現在的Java程序大量使用反射、lambda等技術,已經不是單純OOP語言。

https://mp.weixin.qq.com/s/sbrUX-1a5FodEMmRHn4deQ

用不用lambda,這是一個問題