Java Lambda表達(dá)式

1 簡介

Lambda表達(dá)式是java8提供的新特性,是一種匿名函數(shù)芋绸,也是函數(shù)式接口實(shí)現(xiàn)的快捷方式,類似js中的閉包,它允許把函數(shù)當(dāng)做參數(shù)來使用,是面向函數(shù)式編程的思想证杭,Lambda的格式為: (參數(shù)) -> {方法體}送讲,具有如下特征:

  • 可選類型聲明:不需要聲明參數(shù)類型揭保,編譯器可以統(tǒng)一識別參數(shù)值
  • 可選的參數(shù)圓括號:一個參數(shù)無需定義圓括號魄宏,但多個參數(shù)需要定義圓括號秸侣;
  • 可選的大括號:如果主體包含了一個語句,就不需要使用大括號
  • 可選的返回關(guān)鍵字:如果主體只有一個表達(dá)式返回值則編譯器會自動返回值味榛,大括號需要指定明表達(dá)式返回了一個數(shù)值椭坚。
    例子:
// 1 無參數(shù)的lambda表達(dá)式,返回值5  
() -> 5  
  
// 2 一個數(shù)值類型的參數(shù)搏色,返回其2倍的值  
x -> 2 * x  
  
// 3 兩個數(shù)值類型的參數(shù)善茎,返回差值  
(x, y) -> x – y  
  
// 4 string類型的參數(shù),在控制臺打印频轿,無返回類型  
(String s) -> System.out.print(s)

java是一個面向?qū)ο蟮恼Z言垂涯,而Lambda表達(dá)式卻是一個匿名函數(shù),因此java把Lambda表達(dá)式抽象成一個匿名內(nèi)部類航邢,并沒有破壞面向?qū)ο蟮奶匦浴?/strong>

2 函數(shù)式接口

java8中通過注解@FunctionalInterface標(biāo)明接口為一個函數(shù)式接口耕赘,函數(shù)式接口是只包含一個抽象方法聲明的接口,但是允許有默認(rèn)實(shí)現(xiàn)的方法(比如: java.util.function.BinaryOperator)膳殷。如果定義了多個抽象方法操骡,編譯器會報(bào)錯,如下圖赚窃。所以册招,當(dāng)我們的代碼中定義了函數(shù)式接口的時(shí)候,要記得加上@FunctionalInterface注解勒极,防止被其他人增加額外的方法而破壞函數(shù)式接口的規(guī)范是掰。


錯誤示范

java.lang.Runnable 就是一種函數(shù)式接口,每個 Lambda 表達(dá)式都能隱式地賦值給函數(shù)式接口河质,例如冀惭,我們可以通過 Lambda 表達(dá)式創(chuàng)建 Runnable 接口的引用:

Runnable r = () -> System.out.println("hello world");

當(dāng)不指明函數(shù)式接口時(shí),編譯器會自動進(jìn)行類型推斷掀鹅,如下面代碼,編譯器會自動根據(jù)線程類的構(gòu)造函數(shù)簽名 public Thread(Runnable r) { }媒楼,將該 Lambda 表達(dá)式賦給 Runnable 接口:

new Thread(
   () -> System.out.println("hello world")
).start();

下表列舉了一些常用的函數(shù)式接口

接口 參數(shù) 返回值 示例
Consumer<T> T void Consumer<Integer> c = (int x) -> { System.out.println(x) };
Supplier<T> None T Supplier<Person> supplier = Person::new;
Function<T,R> T R Function<String, Integer> fuc = Integer::parseInt;
BiFunction<T, R, U> T, R U BiFunction<Integer, Integer, Integer> fuc = (x, y) -> x + y;
Predicate<T> T boolean Predicate<Person> p = (Person person) -> { "zhangsan".equals(person.getName()) };

3 Lambda表達(dá)式的作用

Lambda表達(dá)式可以讓代碼更加簡練乐尊,具有可讀性。
舉個栗子划址,這個例子大家都在舉扔嵌,當(dāng)創(chuàng)建新線程時(shí),不使用lambda的情況下:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("為了這一行有用的代碼夺颤,寫了那么多多余的行:(  ");
    }
}).start();

使用lambda表達(dá)式:

new Thread( () -> System.out.println("一起搖擺~~   ") ).start();

4 配合Stream讓代碼整潔到飛起

Stream是Java 8 API添加的一個新的抽象痢缎,稱為流Stream,可以讓我們以一種聲明的方式處理數(shù)據(jù)世澜。Stream類似SQL的存儲過程独旷,提供了一種對 Java 集合運(yùn)算和表達(dá)的高階抽象。

Stream的構(gòu)成

當(dāng)我們使用一個流的時(shí)候,通常包括三個步驟:

  • 獲取一個數(shù)據(jù)源(source)嵌洼,可以是集合案疲,數(shù)組;
// method 1
        Stream<String> stream1 = Stream.of("1", "2", "3");

        // method 2
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        Stream<String> stream2 = list.stream();

        // method 3
        Stream<String> stream3 = list.parallelStream();

        // method 4
        Stream<String> stream4 = Arrays.stream(new String[]{"1", "2"});

        // method 5
        IntStream stream5 = IntStream.range(0, 100);
  • 數(shù)據(jù)轉(zhuǎn)換麻养;
  • 執(zhí)行操作獲取想要的結(jié)果褐啡。

流的操作

  • 中間操作:map (mapToInt, flatMap 等)、 filter鳖昌、 distinct备畦、 sorted、 peek许昨、 limit萍恕、 skip,操作都會返回流對象本身车要。 這樣多個操作可以串聯(lián)成一個管道允粤, 如同流式風(fēng)格(fluent style)
  • 最終操作:forEach、 forEachOrdered翼岁、 toArray类垫、 reduce、 collect琅坡、 min悉患、 max、 count榆俺、 anyMatch售躁、 allMatch、 noneMatch茴晋、 findFirst陪捷、 findAny、 iterator

使用

1 forEach(Consumer<T> action)

public static void main(String[] args) {
        // 輸出Stream中的元素
        Stream.of("1","2","3","4","5").forEach(e->System.out.println(e));
}

2 map(Function<T, R> mapper)

public static void main(String[] args) {
         Stream.of("1","2","3","4","5")
                .map(Integer::parseInt) //轉(zhuǎn)成int
                .forEach(System.out::println);
}

3 flatMap(Function<T, Stream<R>> mapper): 會將每一個輸入對象輸入映射為一個新集合诺擅,然后把這些新集合連成一個大集合

public static void main(String[] args) {
        Stream.of("a-b-c-d","e-f-g-h")
                .flatMap(e->Stream.of(e.split("-")))
                .forEach(e->System.out.print(e));

}
// abcdefgh

4 limit(long maxSize)

public static void main(String[] args) {
        Stream.of(1,2,3,4,5,6)
                .limit(3) 
                .forEach(e->System.out.println(e)); 
}
// 輸出前三個 1市袖,2,3

5 distinct()

Stream.of(1,1,2,3)
                .distinct() //去重
                .forEach(e->System.out.println(e));
// 1 2 3

6 filter(Predicate<T> predicate)

// 可以用and()烁涌、or()邏輯函數(shù)來合并Predicate
Predicate<String> p1 = (n) -> n.startsWith("chang"); //Predicate: 驗(yàn)證傳進(jìn)來的參數(shù)符不符合規(guī)則, 返回true|false
Predicate<String> p2 = (n) -> n.length() == 5;
names.stream()
    .filter(p1.and(p2))
    .forEach((n) -> System.out.print(n));

7 forEachOrdered(Predicate<T> predicate): 適用用于并行流的情況下進(jìn)行迭代苍碟,能保證迭代的有序性

Stream.of(0,1,2,3,4,5,6,7,8,9)
                .parallel()
                .forEachOrdered(e->{
                    System.out.println(Thread.currentThread().getName()+": "+e);});

8 Collectors: 將流轉(zhuǎn)換成集合和聚合元素。Collectors 可用于返回列表或字符串

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
 
System.out.println("篩選列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);

// 篩選列表: [abc, bc, efg, abcd, jkl]
// 合并字符串: abc, bc, efg, abcd, jkl


List<Person> personList = Lists.newArrayList();

// 同名分組
Map<String, List<Person>> map = personList.stream().collect(Collectors.groupingBy(Person::getName));

// 根據(jù)唯一id映射, 保證Person::getId是唯一的撮执,否則報(bào)java.lang.IllegalStateException
Map<Integer, Person> map = personList.stream().collect(Collectors.toMap(Person::getId, p -> p));

9 更多例子

String k = "key";
HashMap<String, Integer> map = new HashMap<>() ;
map.put(k, 1);
map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);

// 等同如下代碼
String k = "key";
HashMap<String, Integer> map = new HashMap<>() ;
map.put(k, 1);
int newVal = 2;
if(map.containsKey(k)) {
    map.put(k, map.get(k) + newVal);
} else {
    map.put(k, newVal);
}

復(fù)合lambda表達(dá)式

1 比較器復(fù)合

可以使用靜態(tài)方法Comparator.comparing定義比較器微峰,根據(jù)提取用于比較的鍵值的Function來返回一個Comparator,
如: Comparator<Apple> cmp = Comparator.comparing(Apple::getWeight)抒钱,該表達(dá)式表示比較蘋果的重量蜓肆,如果想對蘋果按重量遞減排序颜凯,此時(shí)無需重新定義比較器,接口有一個默認(rèn)方法reversed可以使給定的比較器逆序症杏。因此仍然用開始的那個比較器装获,只要簡單修改修改一下前一個例子就可以對蘋果按重量遞減排序: appleList.sort(cmp.reversed());

2 謂詞復(fù)合

謂詞接口包括三個方法:negate、and和or厉颤,讓我們可以重用已有的Predicate來創(chuàng)建更復(fù)雜的謂詞穴豫,如4.3.6中的filter方法

3 函數(shù)復(fù)合

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); // andThen方法會返回一個函數(shù),它先對輸入應(yīng)用一個給定函數(shù)逼友,再對輸出應(yīng)用另一個函數(shù)
int result = h.apply(1); // 返回結(jié)果為4

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g); // compose方法精肃,先把給定的函數(shù)用作compose的參數(shù)里面給的那個函數(shù),然后再把函數(shù)本身用于結(jié)果
int result = h.apply(1);  // 返回結(jié)果為3
函數(shù)復(fù)合

總結(jié)

  • stream只能遍歷一次帜乞,遍歷完之后司抱,這個流已經(jīng)被消費(fèi)掉了。 可以從原始數(shù)據(jù)源那里再獲得一個新的流來重新遍歷一遍黎烈,重復(fù)遍歷會報(bào)IllegalStateException
List<String> title = Arrays.asList("1", "2", "3");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println); // Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
  • 簡潔的lambda可以增加代碼可讀性习柠,但這簡歷在團(tuán)隊(duì)成員對lambda都比較熟悉的基礎(chǔ)上,如果寫了太復(fù)雜的表達(dá)式照棋,其他同學(xué)維護(hù)和理解起來也會增加相應(yīng)的成本资溃。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烈炭,隨后出現(xiàn)的幾起案子溶锭,更是在濱河造成了極大的恐慌,老刑警劉巖符隙,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趴捅,死亡現(xiàn)場離奇詭異,居然都是意外死亡霹疫,警方通過查閱死者的電腦和手機(jī)拱绑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來更米,“玉大人欺栗,你說我怎么就攤上這事≌髀停” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵消请,是天一觀的道長栏笆。 經(jīng)常有香客問我,道長臊泰,這世上最難降的妖魔是什么蛉加? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上针饥,老公的妹妹穿的比我還像新娘厂抽。我一直安慰自己,他們只是感情好丁眼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布筷凤。 她就那樣靜靜地躺著,像睡著了一般苞七。 火紅的嫁衣襯著肌膚如雪藐守。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天蹂风,我揣著相機(jī)與錄音卢厂,去河邊找鬼。 笑死惠啄,一個胖子當(dāng)著我的面吹牛慎恒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撵渡,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼融柬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了姥闭?” 一聲冷哼從身側(cè)響起丹鸿,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棚品,沒想到半個月后靠欢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铜跑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年门怪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锅纺。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡掷空,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出囤锉,到底是詐尸還是另有隱情坦弟,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布官地,位于F島的核電站酿傍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驱入。R本人自食惡果不足惜赤炒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一氯析、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莺褒,春花似錦掩缓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旷余,卻和暖如春绢记,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背正卧。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工蠢熄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炉旷。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓签孔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窘行。 傳聞我的和親對象是個殘疾皇子饥追,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容