Java8:lambda表達(dá)式

基本定義:

簡(jiǎn)單理解:lambda表達(dá)式相當(dāng)于創(chuàng)建的一個(gè)匿名對(duì)象.
每個(gè)lambda表達(dá)式都與一個(gè)特定的Java接口相關(guān)聯(lián),同時(shí)這個(gè)函數(shù)式接口只能包含一個(gè)抽象方法聲明∠危可以在接口上添加@FunctionalInterface注解注明此接口為lambda表達(dá)式相關(guān)接口。
例如:
聲明一個(gè)函數(shù)式接口Converter:

@FunctionalInterface
public interface Converter<F, T> {

    T convertor(F from);

//    T convertor2(F from);

}

通過lambda表達(dá)式生成一個(gè)Converter類型的對(duì)象:

        Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
        Integer converted = converter.convertor("8");
        System.out.println(converted);

Java8允許通過 :: 關(guān)鍵字傳遞方法或者構(gòu)造函數(shù)的引用。

傳遞靜態(tài)方法

例如:

        Converter<String, Integer> staticFunConverter = Integer::valueOf;
        Integer staticFunConverted = staticFunConverter.convertor("123");
        System.out.println(converted);   // 123

傳遞對(duì)象方法

例如:
新建一個(gè)類Something

 static class Something {
        String startsWith(String s) {
            return String.valueOf(s.charAt(0));
        }
    }

通過:: 關(guān)鍵字引用Something類型對(duì)象的方法:

    Something something = new Something();
        Converter<String, String> objFunConverter = something::startsWith;
        String objFunConverted = objFunConverter.convertor("Java");
        System.out.println(objFunConverted);//J

傳遞構(gòu)造方法

先定義一個(gè)多構(gòu)造函數(shù)的bean

    static class Person {
        String firstName;
        String lastName;

        Person() {
        }

        Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }

然后定義一個(gè)可以創(chuàng)建person 的personFacotory接口

public interface PersonFactory<P extends FuncTest.Person> {

    P create(String firstName, String lastName);

}

通過構(gòu)造函數(shù)引用富俄,實(shí)現(xiàn)工廠接口。

PersonFactory<Person> personFactory = Person::new;
        Person person = personFactory.create("pee", "hao");
        System.out.println(person.lastName);//hao

上面的例子中通過Person::new創(chuàng)建一個(gè)Person類構(gòu)造函數(shù)的引用而咆,Java編譯器會(huì)根據(jù)PersonFactory.create方法的簽名自動(dòng)選擇合適的構(gòu)造函數(shù)霍比。

lambda表達(dá)式訪問變量范圍

lambda表達(dá)式訪問變量范圍和匿名內(nèi)部類相同。

可訪問本地final局部變量暴备。

final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

可訪問成員變量和靜態(tài)變量悠瞬。

lambda表達(dá)式不能訪問默認(rèn)接口方法。

帶有擴(kuò)展方法的接口

public interface Formula {

    double calculate(int a);

    //JAVA8中可以通過default關(guān)鍵字在接口中添加一個(gè)非抽象方法(即:擴(kuò)展方法)
    default double sqrt(int a) {
        return Math.sqrt(a);
    }

}
Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a);
    }
};
System.out.println(formula.calculate(9));
System.out.println(formula.sqrt(9));

Formula formula1 = Math::sqrt;//此處無(wú)法通過直接調(diào)用擴(kuò)展方法sqrt實(shí)現(xiàn)涯捻。

Java8內(nèi)置的函數(shù)式接口

已經(jīng)存在的Comparator接口和Runnable接口浅妆。

Predicates(斷言)

Predicates是包含一個(gè)參數(shù)返回值為boolean型的函數(shù),為了滿足復(fù)雜的邏輯條件(or and negate)Predicates接口包含各種默認(rèn)方法汰瘫。

 //Predicates(斷言)
        Predicate<String> predicate = (s -> s.length() > 0);
        System.out.println(predicate.test("foo"));//true
        System.out.println(predicate.negate().test("foo"));//false

Functions

Functions接口接收一個(gè)參數(shù)并生成一個(gè)結(jié)果狂打,可以使用Functions接口的默認(rèn)方法(compose, andThen)將多個(gè)functions串聯(lián)起來(lái)。
注:compose, andThen的區(qū)別混弥,compose方法自身先執(zhí)行趴乡,再把執(zhí)行結(jié)果傳遞給調(diào)用者執(zhí)行。andThen先執(zhí)行前一步的調(diào)用者蝗拿,獲取前一步執(zhí)行結(jié)果后再執(zhí)行晾捏。

//Functions
        Function<Integer, Integer> time2 = e -> e * 2;
        Function<Integer, Integer> squared = e -> e * e;

        System.out.println(time2.compose(squared).apply(4));//32
        System.out.println(time2.andThen(squared).apply(4));//64

Consumers

Consumers接口表示在單個(gè)輸入?yún)?shù)上要執(zhí)行的操作。

//Consumers
        Consumer<FuncTest.Person> greeter = person -> System.out.println("hello :" + person.firstName);
        greeter.accept(new FuncTest.Person("jiahong", "hao"));//hello:jiahong

Comparators

Optionals

Optionals不是一個(gè)函數(shù)式接口哀托,而是一個(gè)用來(lái)防止出現(xiàn)NullPointerException異常的很好工具惦辛。

Streams

java.util.Stream表示可以執(zhí)行一個(gè)或多個(gè)操作的序列元素,stream的操作可以分為兩種類型:中間操作或最終操作仓手。中間操作的返回值還是一個(gè)stream胖齐,最終操作的返回值是一個(gè)確定的類型∷悦埃可以通過鏈?zhǔn)秸{(diào)用可以將多個(gè)中間操作串聯(lián)起來(lái)呀伙。stream上的操作既可以串行執(zhí)行也可以是并行執(zhí)行。

List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
Filter

Filter通過斷言來(lái)對(duì)stream中所有的元素進(jìn)行判斷添坊,由于filter是一個(gè)中間操作剿另,因此我們可以在filter后面再調(diào)用forEach操作,F(xiàn)orEach操作接收一個(gè)consumer接口作為參數(shù),這個(gè)consumer會(huì)在所有通過filter的元素上執(zhí)行雨女。ForEach是一個(gè)最終操作谚攒,返回值類型是void,因此我們不能在ForEach后面再調(diào)用其他操作氛堕。(最終操作是回去執(zhí)行的操作馏臭。這里的forEach相當(dāng)于for(...)循環(huán)了)

        stringCollection
                .stream()   
                .filter(s -> s.startsWith("a"))
                .forEach(System.out::println);
        //aaa2 aaa1
Sorted

Sorted是一個(gè)中間操作,sorted操作接收Comparator接口作為參數(shù)讼稚,返回一個(gè)有序的stream位喂。如果傳遞一個(gè)空函數(shù)給sorted,會(huì)按照自然順序進(jìn)行排序乱灵,如果元素不支持排序,會(huì)在最終操作時(shí)拋出異常七冲。

stringCollection
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// aaa1 aaa2
Map

map是一個(gè)中間操作痛倚,通過給定的轉(zhuǎn)換方法map能將stream中的每個(gè)元素映射為另外一種對(duì)象,下面的例子中將每個(gè)string轉(zhuǎn)換為upper-cased string澜躺。map也支持不同類型的映射蝉稳,具體映射的目標(biāo)類型取決于映射方法的返回值類型。

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
Match

matching是一個(gè)最終操作掘鄙,返回值類型為boolean型耘戚,可以用來(lái)確定某種斷言是否與stream匹配。

boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));

System.out.println(allStartsWithA);      // false

boolean noneStartsWithZ =
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));

System.out.println(noneStartsWithZ);      // true
Count

Count是一個(gè)最終操作操漠,返回stream中元素的個(gè)數(shù)收津,返回值類型為long。

long startsWithB =
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();

System.out.println(startsWithB);    // 3
Reduce

Reduce是一個(gè)最終操作浊伙,通過給定的方法在stream上執(zhí)行reduce操作撞秋,返回值為使用Optional接口包裝的reduce值。(Reduce含義:把...歸納為,減少)

Optional<String> reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

Parallel Streams

Parallel Streams能在多個(gè)線程上并發(fā)地執(zhí)行嚣鄙,下面的例子中通過簡(jiǎn)單的使用parallel streams就能大幅度地提高系統(tǒng)性能吻贿。
首先我們創(chuàng)建一個(gè)很大的元素唯一的list.

int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
    UUID uuid = UUID.randomUUID();
    values.add(uuid.toString());
}

我們開始測(cè)試在這個(gè)stream上執(zhí)行排序所需的時(shí)間
Sequential Sort

long t0 = System.nanoTime();

long count = values.stream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));

// sequential sort took: 899 ms

Parallel Sort

long t0 = System.nanoTime();

long count = values.parallelStream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("parallel sort took: %d ms", millis));

// parallel sort took: 472 ms

上面的兩段代碼幾乎相同,但是parallel sort的速度幾乎比sequential Sort速度快50%哑子。

Map

Map<Integer, String> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
                //當(dāng)鍵值在map中不存在時(shí),才會(huì)執(zhí)行put操作.
            map.putIfAbsent(i, "val" + i);
        }

        map.forEach((key, value) -> System.out.println(value));

        map.computeIfPresent(3, (num, val) -> val + num + 1);
        System.out.println(map.get(3)); // val331 函數(shù)式接口返回值與原來(lái)的value不同,則賦予新值.

        map.computeIfPresent(9, (num, val) -> null);
        System.out.println(map.containsKey(9));//false 函數(shù)式接口返回值為null,則相應(yīng)的mapping會(huì)被刪除


        //當(dāng)鍵值在map中不存在時(shí),才會(huì)計(jì)算函數(shù)式接口的值,并設(shè)值.
        map.computeIfAbsent(23, num -> "val" + num);
        System.out.println(map.containsKey(23));
        System.out.println(map.get(23));

        map.computeIfAbsent(3, num -> null);
        System.out.println(map.get(3));//val331

        map.computeIfAbsent(3, num -> "bam");
        System.out.println(map.get(3));//val331

下面的例子展示了map如何刪除value不為空的key的操作舅列。

map.remove(3, "val3");
map.get(3);             // val33

map.remove(3, "val33");
map.get(3);             // null
Another helpful method:

System.out.println(map.getOrDefault(42, "not found"));  // not found
System.out.println(map.get(42));//null

//Merging entries of a map is quite easy:
map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9concat

merge方法在key不存在時(shí),可以向map中添加key/value卧蜓,當(dāng)key存在時(shí)帐要,可以對(duì)這個(gè)key對(duì)應(yīng)的value執(zhí)行merge操作。

todo:stream()方法中的collect()需要總結(jié)下烦却,涉及到了經(jīng)常要用到的list裝換map

JAVA8 教程
Java8 lambda表達(dá)式10個(gè)示例
【譯】Java 8的新特性—終極版(杜琪)
Java8初體驗(yàn)(二)Stream語(yǔ)法詳解
Java8 新特性之流式數(shù)據(jù)處理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宠叼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冒冬,老刑警劉巖伸蚯,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異简烤,居然都是意外死亡剂邮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門横侦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)挥萌,“玉大人,你說(shuō)我怎么就攤上這事枉侧∫伲” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵榨馁,是天一觀的道長(zhǎng)憨栽。 經(jīng)常有香客問我,道長(zhǎng)翼虫,這世上最難降的妖魔是什么屑柔? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮珍剑,結(jié)果婚禮上掸宛,老公的妹妹穿的比我還像新娘。我一直安慰自己招拙,他們只是感情好唧瘾,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著别凤,像睡著了一般劈愚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闻妓,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天菌羽,我揣著相機(jī)與錄音,去河邊找鬼由缆。 笑死注祖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的均唉。 我是一名探鬼主播是晨,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舔箭!你這毒婦竟也來(lái)了罩缴?” 一聲冷哼從身側(cè)響起蚊逢,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎箫章,沒想到半個(gè)月后烙荷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡檬寂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年终抽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桶至。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昼伴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出镣屹,到底是詐尸還是另有隱情圃郊,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布女蜈,位于F島的核電站描沟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鞭光。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一泞遗、第九天 我趴在偏房一處隱蔽的房頂上張望惰许。 院中可真熱鬧,春花似錦史辙、人聲如沸汹买。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晦毙。三九已至,卻和暖如春耙蔑,著一層夾襖步出監(jiān)牢的瞬間见妒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工甸陌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留须揣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓钱豁,卻偏偏與公主長(zhǎng)得像耻卡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牲尺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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