1. 程式人生 > >最全最強 Java 8 - 函式程式設計(lambda表示式)

最全最強 Java 8 - 函式程式設計(lambda表示式)

Java 8 - 函式程式設計(lambda表示式)

我們關心的是如何寫出好程式碼,而不是符合函式程式設計風格的程式碼。 @pdai

  • Java 8 - 函式程式設計(lambda表示式)
    • 簡介
    • lambda表示式
    • 分類
      • 惰性求值方法
      • 及早求值方法
    • stream & parallelStream
      • stream & parallelStream
      • parallelStream原理:
      • stream與parallelStream效能測試對比
      • Stream中常用方法如下:
    • 常用例子
      • 匿名類簡寫
      • forEach
      • 方法引用
      • Filter & Predicate
      • Map&Reduce
      • Collectors
      • flatMap
      • distinct
      • count
      • Match
      • min,max,summaryStatistics
    • 參考資料

最全的Java後端知識體系 https://www.pdai.tech, 每天更新中...。

簡介

在Java世界裡面,面向物件還是主流思想,對於習慣了面向物件程式設計的開發者來說,抽象的概念並不陌生。面向物件程式設計是對資料進行抽象,而函數語言程式設計是對行為進行抽象。現實世界中,資料和行為並存,程式也是如此,因此這兩種程式設計方式我們都得學。

這種新的抽象方式還有其他好處。很多人不總是在編寫效能優先的程式碼,對於這些人來說,函數語言程式設計帶來的好處尤為明顯。程式設計師能編寫出更容易閱讀的程式碼——這種程式碼更多地表達了業務邏輯,而不是從機制上如何實現。易讀的程式碼也易於維護、更可靠、更不容易出錯。

在寫回調函式和事件處理器時,程式設計師不必再糾纏於匿名內部類的冗繁和可讀性,函數語言程式設計讓事件處理系統變得更加簡單。能將函式方便地傳遞也讓編寫惰性程式碼變得容易,只有在真正需要的時候,才初始化變數的值。

面向物件程式設計是對資料進行抽象;函數語言程式設計是對行為進行抽象。

核心思想:使用不可變值和函式,函式對一個值進行處理,對映成另一個值。

對核心類庫的改進主要包括集合類的API和新引入的流Stream。流使程式設計師可以站在更高的抽象層次上對集合進行操作。

lambda表示式

  • lambda表示式僅能放入如下程式碼:預定義使用了 @Functional 註釋的函式式介面,自帶一個抽象函式的方法,或者SAM(Single Abstract Method 單個抽象方法)型別。這些稱為lambda表示式的目標型別,可以用作返回型別,或lambda目的碼的引數。例如,若一個方法接收Runnable、Comparable或者 Callable 介面,都有單個抽象方法,可以傳入lambda表示式。類似的,如果一個方法接受聲明於 java.util.function 包內的介面,例如 Predicate、Function、Consumer 或 Supplier,那麼可以向其傳lambda表示式。

  • lambda表示式內可以使用方法引用,僅當該方法不修改lambda表示式提供的引數。本例中的lambda表示式可以換為方法引用,因為這僅是一個引數相同的簡單方法呼叫。
list.forEach(n -> System.out.println(n)); 
list.forEach(System.out::println);  // 使用方法引用

然而,若對引數有任何修改,則不能使用方法引用,而需鍵入完整地lambda表示式,如下所示:

list.forEach((String s) -> System.out.println("*" + s + "*"));

事實上,可以省略這裡的lambda引數的型別宣告,編譯器可以從列表的類屬性推測出來。

  • lambda內部可以使用靜態、非靜態和區域性變數,這稱為lambda內的變數捕獲。

  • Lambda表示式在Java中又稱為閉包或匿名函式,所以如果有同事把它叫閉包的時候,不用驚訝。

  • Lambda方法在編譯器內部被翻譯成私有方法,並派發 invokedynamic 位元組碼指令來進行呼叫。可以使用JDK中的 javap 工具來反編譯class檔案。使用 javap -p 或 javap -c -v 命令來看一看lambda表示式生成的位元組碼。大致應該長這樣:
private static java.lang.Object lambda$0(java.lang.String);
  • lambda表示式有個限制,那就是隻能引用 final 或 final 區域性變數,這就是說不能在lambda內部修改定義在域外的變數。
List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { factor++; });

Compile time error : "local variables referenced from a lambda expression must be final or effectively final"
另外,只是訪問它而不作修改是可以的,如下所示:

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { System.out.println(factor*element); });

分類

惰性求值方法

lists.stream().filter(f -> f.getName().equals("p1"))

如上示例,這行程式碼並未做什麼實際性的工作,filter只是描述了Stream,沒有產生新的集合。

如果是多個條件組合,可以通過程式碼塊{}

及早求值方法

List<Person> list2 = lists.stream().filter(f -> f.getName().equals("p1")).collect(Collectors.toList());

如上示例,collect最終會從Stream產生新值,擁有終止操作。

理想方式是形成一個惰性求值的鏈,最後用一個及早求值的操作返回想要的結果。與建造者模式相似,建造者模式先是使用一系列操作設定屬性和配置,最後呼叫build方法,建立物件。

stream & parallelStream

stream & parallelStream

每個Stream都有兩種模式:順序執行和並行執行。

順序流:

List <Person> people = list.getStream.collect(Collectors.toList());

並行流:

List <Person> people = list.getStream.parallel().collect(Collectors.toList());

顧名思義,當使用順序方式去遍歷時,每個item讀完後再讀下一個item。而使用並行去遍歷時,陣列會被分成多個段,其中每一個都在不同的執行緒中處理,然後將結果一起輸出。

parallelStream原理:

List originalList = someData;
split1 = originalList(0, mid);//將資料分小部分
split2 = originalList(mid,end);
new Runnable(split1.process());//小部分執行操作
new Runnable(split2.process());
List revisedList = split1 + split2;//將結果合併

大家對hadoop有稍微瞭解就知道,裡面的 MapReduce 本身就是用於並行處理大資料集的軟體框架,其 處理大資料的核心思想就是大而化小,分配到不同機器去執行map,最終通過reduce將所有機器的結果結合起來得到一個最終結果,與MapReduce不同,Stream則是利用多核技術可將大資料通過多核並行處理,而MapReduce則可以分散式的。

stream與parallelStream效能測試對比

如果是多核機器,理論上並行流則會比順序流快上一倍,下面是測試程式碼

long t0 = System.nanoTime();

//初始化一個範圍100萬整數流,求能被2整除的數字,toArray()是終點方法

int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray();

long t1 = System.nanoTime();

//和上面功能一樣,這裡是用並行流來計算

int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray();

long t2 = System.nanoTime();

//我本機的結果是serial: 0.06s, parallel 0.02s,證明並行流確實比順序流快

System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);

Stream中常用方法如下:

  • stream(), parallelStream()
  • filter()
  • findAny() findFirst()
  • sort
  • forEach void
  • map(), reduce()
  • flatMap() - 將多個Stream連線成一個Stream
  • collect(Collectors.toList())
  • distinct, limit
  • count
  • min, max, summaryStatistics

看下所有API:

常用例子

匿名類簡寫

new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

// 用法
(params) -> expression
(params) -> statement
(params) -> { statements }

forEach

// forEach
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
 
// 使用Java 8的方法引用更方便,方法引用由::雙冒號操作符標示,
features.forEach(System.out::println);

方法引用

構造引用

// Supplier<Student> s = () -> new Student();
Supplier<Student> s = Student::new;

物件::例項方法 Lambda表示式的(形參列表)與例項方法的(實參列表)型別,個數是對應

// set.forEach(t -> System.out.println(t));
set.forEach(System.out::println);

類名::靜態方法

// Stream<Double> stream = Stream.generate(() -> Math.random());
Stream<Double> stream = Stream.generate(Math::random);

類名::例項方法

//  TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2));
/*  這裡如果使用第一句話,編譯器會有提示:Can be replaced with Comparator.naturalOrder,這句話告訴我們
  String已經重寫了compareTo()方法,在這裡寫是多此一舉,這裡為什麼這麼寫,是因為為了體現下面
  這句編譯器的提示:Lambda can be replaced with method reference。好了,下面的這句就是改寫成方法引用之後:
*/
TreeSet<String> set = new TreeSet<>(String::compareTo);

Filter & Predicate

常規用法

public static void main(args[]){
    List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
 
    System.out.println("Languages which starts with J :");
    filter(languages, (str)->str.startsWith("J"));
 
    System.out.println("Languages which ends with a ");
    filter(languages, (str)->str.endsWith("a"));
 
    System.out.println("Print all languages :");
    filter(languages, (str)->true);
 
    System.out.println("Print no language : ");
    filter(languages, (str)->false);
 
    System.out.println("Print language whose length greater than 4:");
    filter(languages, (str)->str.length() > 4);
}
 
public static void filter(List names, Predicate condition) {
    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
        System.out.println(name + " ");
    });
}

多個Predicate組合filter

// 可以用and()、or()和xor()邏輯函式來合併Predicate,
// 例如要找到所有以J開始,長度為四個字母的名字,你可以合併兩個Predicate並傳入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));

Map&Reduce

map將集合類(例如列表)元素進行轉換的。還有一個 reduce() 函式可以將所有值合併成一個

List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);

Collectors

// 將字串換成大寫並用逗號連結起來
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
  • Collectors.joining(", ")
  • Collectors.toList()
  • Collectors.toSet() ,生成set集合
  • Collectors.toMap(MemberModel::getUid, Function.identity())
  • Collectors.toMap(ImageModel::getAid, o -> IMAGE_ADDRESS_PREFIX + o.getUrl())

flatMap

將多個Stream連線成一個Stream

List<Integer> result= Stream.of(Arrays.asList(1,3),Arrays.asList(5,6)).flatMap(a->a.stream()).collect(Collectors.toList());

結果: [1, 3, 5, 6]

distinct

去重

List<LikeDO> likeDOs=new ArrayList<LikeDO>();
List<Long> likeTidList = likeDOs.stream().map(LikeDO::getTid)
                .distinct().collect(Collectors.toList());

count

計總數

int countOfAdult=persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .count();

Match

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

min,max,summaryStatistics

最小值,最大值

List<Person> lists = new ArrayList<Person>();
lists.add(new Person(1L, "p1"));
lists.add(new Person(2L, "p2"));
lists.add(new Person(3L, "p3"));
lists.add(new Person(4L, "p4"));
Person a = lists.stream().max(Comparator.comparing(t -> t.getId())).get();
System.out.println(a.getId());

如果比較器涉及多個條件,比較複雜,可以定製

 Person a = lists.stream().min(new Comparator<Person>() {

      @Override
      public int compare(Person o1, Person o2) {
           if (o1.getId() > o2.getId()) return -1;
           if (o1.getId() < o2.getId()) return 1;
           return 0;
       }
 }).get();

summaryStatistics

//獲取數字的個數、最小值、最大值、總和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());

peek

可以使用peek方法,peek方法可只包含一個空的方法體,只要能設定斷點即可,但有些IDE不允許空,可以如下文示例,簡單寫一個列印邏輯。

注意,除錯完後要刪掉。

List<Person> lists = new ArrayList<Person>();
lists.add(new Person(1L, "p1"));
lists.add(new Person(2L, "p2"));
lists.add(new Person(3L, "p3"));
lists.add(new Person(4L, "p4"));
System.out.println(lists);

List<Person> list2 = lists.stream()
                 .filter(f -> f.getName().startsWith("p"))
                .peek(t -> {
                    System.out.println(t.getName());
                })
                .collect(Collectors.toList());
System.out.println(list2);

FunctionalInterface

理解註解 @FunctionInterface

/**
 * An informative annotation type used to indicate that an interface
 * type declaration is intended to be a <i>functional interface</i> as
 * defined by the Java Language Specification.
 *
 * Conceptually, a functional interface has exactly one abstract
 * method.  Since {@linkplain java.lang.reflect.Method#isDefault()
 * default methods} have an implementation, they are not abstract.  If
 * an interface declares an abstract method overriding one of the
 * public methods of {@code java.lang.Object}, that also does
 * <em>not</em> count toward the interface's abstract method count
 * since any implementation of the interface will have an
 * implementation from {@code java.lang.Object} or elsewhere.
 *
 * <p>Note that instances of functional interfaces can be created with
 * lambda expressions, method references, or constructor references.
 *
 * <p>If a type is annotated with this annotation type, compilers are
 * required to generate an error message unless:
 *
 * <ul>
 * <li> The type is an interface type and not an annotation type, enum, or class.
 * <li> The annotated type satisfies the requirements of a functional interface.
 * </ul>
 *
 * <p>However, the compiler will treat any interface meeting the
 * definition of a functional interface as a functional interface
 * regardless of whether or not a {@code FunctionalInterface}
 * annotation is present on the interface declaration.
 *
 * @jls 4.3.2. The Class Object
 * @jls 9.8 Functional Interfaces
 * @jls 9.4.3 Interface Method Body
 * @since 1.8
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface{}
  • interface做註解的註解型別,被定義成java語言規範

  • 一個被它註解的介面只能有一個抽象方法,有兩種例外
  • 第一是介面允許有實現的方法,這種實現的方法是用default關鍵字來標記的(java反射中java.lang.reflect.Method#isDefault()方法用來判斷是否是default方法)
  • 第二如果宣告的方法和java.lang.Object中的某個方法一樣,它可以不當做未實現的方法,不違背這個原則:一個被它註解的介面只能有一個抽象方法, 比如:
    java public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }

  • 如果一個型別被這個註解修飾,那麼編譯器會要求這個型別必須滿足如下條件:
    • 這個型別必須是一個interface,而不是其他的註解型別、列舉enum或者類class
    • 這個型別必須滿足function interface的所有要求,如你個包含兩個抽象方法的介面增加這個註解,會有編譯錯誤。
  • 編譯器會自動把滿足function interface要求的介面自動識別為function interface,所以你才不需要對上面示例中的 ITest介面增加@FunctionInterface註解。

自定義函式介面

@FunctionalInterface
public interface IMyInterface {
    void study();
}

package com.isea.java;
public class TestIMyInterface {
    public static void main(String[] args) {
        IMyInterface iMyInterface = () -> System.out.println("I like study");
        iMyInterface.study();
    }
}

內建四大函式介面

  • 消費型介面:Consumer< T> void accept(T t)有引數,無返回值的抽象方法;

    比如:map.forEach(BiConsumer<A, T>)

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
  • 供給型介面:Supplier < T> T get() 無參有返回值的抽象方法;

    以stream().collect(Collector<? super T, A, R> collector)為例:

比如:

Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person

再如:

// 呼叫方法
<R, A> R collect(Collector<? super T, A, R> collector)

// Collectors.toSet
public static <T>
    Collector<T, ?, Set<T>> toSet() {
        return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_UNORDERED_ID);
}

// CollectorImpl
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final BinaryOperator<A> combiner;
private final Function<A, R> finisher;
private final Set<Characteristics> characteristics;

CollectorImpl(Supplier<A> supplier,
              BiConsumer<A, T> accumulator,
              BinaryOperator<A> combiner,
              Function<A,R> finisher,
              Set<Characteristics> characteristics) {
    this.supplier = supplier;
    this.accumulator = accumulator;
    this.combiner = combiner;
    this.finisher = finisher;
    this.characteristics = characteristics;
}

CollectorImpl(Supplier<A> supplier,
              BiConsumer<A, T> accumulator,
              BinaryOperator<A> combiner,
              Set<Characteristics> characteristics) {
    this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}

// collect()方法實現
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
    A container;
    if (isParallel()
            && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
            && (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
        container = collector.supplier().get();
        BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
        forEach(u -> accumulator.accept(container, u));
    }
    else {
        container = evaluate(ReduceOps.makeRef(collector));
    }
    return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
           ? (R) container
           : collector.finisher().apply(container);
}
  • 斷定型介面: Predicate<T> boolean test(T t):有參,但是返回值型別是固定的boolean

    比如:steam().filter()中引數就是Predicate

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
  • 函式型介面: Function<T,R> R apply(T t)有參有返回值的抽象方法;

    比如: steam().map() 中引數就是Function<? super T, ? extends R>;reduce()中引數BinaryOperator<T> (ps: BinaryOperator<T> extends BiFunction<T,T,T>)

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

一些例子

  • 輸出 年齡>25的女程式設計師中名字排名前3位的姓名
javaProgrammers.stream()
          .filter((p) -> (p.getAge() > 25))
          .filter((p) -> ("female".equals(p.getGender())))
          .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
          .limit(3)
          //.forEach(e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary()))//漲工資
          .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
  • 工資最高的 Java programmer
Person person = javaProgrammers
          .stream()
          .max((p, p2) -> (p.getSalary() - p2.getSalary()))
          .get()
  • 將 Java programmers 的 first name 存放到 TreeSet
TreeSet<String> javaDevLastName = javaProgrammers
          .stream()
          .map(Person::getLastName)
          .collect(toCollection(TreeSet::new))
  • 計算付給 Java programmers 的所有money
int totalSalary = javaProgrammers
          .parallelStream()
          .mapToInt(p -> p.getSalary())
          .sum();
  • Comparator多屬性排序: 先按名字不分大小寫排,再按GID倒序排,最後按年齡正序排
public static void main(String[] args) {
    List<Person> personList = getTestList();
    personList.sort(Comparator.comparing(Person::getName, String.CASE_INSENSITIVE_ORDER)
            .thenComparing(Person::getGid, (a, b) -> b.compareTo(a))
            .thenComparingInt(Person::getAge));
    personList.stream().forEach(System.out::println);
}

public static List<Person> getTestList() {
    return Lists.newArrayList(new Person("dai", "301", 10), new Person("dai", "303", 10),
            new Person("dai", "303", 8), new Person("dai", "303", 6), new Person("dai", "303", 11),
            new Person("dai", "302", 9), new Person("zhang", "302", 9), new Person("zhang", "301", 9),
            new Person("Li", "301", 8));
}

// 輸出結果
// Person [name=dai, gid=303, age=6]
// Person [name=dai, gid=303, age=8]
// Person [name=dai, gid=303, age=10]
// Person [name=dai, gid=303, age=11]
// Person [name=dai, gid=302, age=9]
// Person [name=dai, gid=301, age=10]
// Person [name=Li, gid=301, age=8]
// Person [name=zhang, gid=302, age=9]
// Person [name=zhang, gid=301, age=9]
  • 處理字串

兩個新的方法可在字串類上使用:join和chars。第一個方法使用指定的分隔符,將任何數量的字串連線為一個字串。

String.join(":", "foobar", "foo", "bar");
// => foobar:foo:bar

第二個方法chars從字串所有字元建立資料流,所以你可以在這些字元上使用流式操作。

"foobar:foo:bar"
    .chars()
    .distinct()
    .mapToObj(c -> String.valueOf((char)c))
    .sorted()
    .collect(Collectors.joining());
// => :abfor

不僅僅是字串,正則表示式模式串也能受益於資料流。我們可以分割任何模式串,並建立資料流來處理它們,而不是將字串分割為單個字元的資料流,像下面這樣:

Pattern.compile(":")
    .splitAsStream("foobar:foo:bar")
    .filter(s -> s.contains("bar"))
    .sorted()
    .collect(Collectors.joining(":"));
// => bar:foobar

此外,正則模式串可以轉換為謂詞。這些謂詞可以像下面那樣用於過濾字串流:

Pattern pattern = Pattern.compile(".*@gmail\\.com");
Stream.of("[email protected]", "[email protected]")
    .filter(pattern.asPredicate())
    .count();
// => 1

上面的模式串接受任何以@gmail.com結尾的字串,並且之後用作Java8的Predicate來過濾電子郵件地址流。

  • Local Cache實現
public class TestLocalCache {

    private static ConcurrentHashMap<Integer, Long> cache = new ConcurrentHashMap<>();

    static long fibonacci(int i) {
        if (i == 0)
            return i;

        if (i == 1)
            return 1;

        return cache.computeIfAbsent(i, (key) -> {
            System.out.println("Slow calculation of " + key);

            return fibonacci(i - 2) + fibonacci(i - 1);
        });
    }
    
    public static void main(String[] args) {
        // warm up
        for (int i = 0; i < 101; i++)
            System.out.println(
                "f(" + i + ") = " + fibonacci(i));
        
        // read -> cal
        long current = System.currentTimeMillis();
        System.out.println(fibonacci(100));
        System.out.println(System.currentTimeMillis()-current);
    }
}
  • 集合--》取元素的一個屬性--》去重---》組裝成List--》返回
List<LikeDO> likeDOs=new ArrayList<LikeDO>();
List<Long> likeTidList = likeDOs.stream().map(LikeDO::getTid)
                .distinct().collect(Collectors.toList());
  • 集合--》按表示式過濾--》遍歷、每個元系處理--》放入預先定義的集合中
  Map<String, StkProduct> newStockName2Product = Maps.newConcurrentMap();
        stockProducts.stream().filter(stkProduct -> stkProduct.enabled).forEach(stkProduct -> {
            String newName = BCConvert.bj2qj(StringUtils.replace(stkProduct.name, " ", ""));
            newStockName2Product.put(newName, stkProduct);
        });
 Set<String> qjStockNames;
 qjStockNames.stream().filter(name -> !acAutomaton.getKey2link().containsKey(name)).forEach(name -> {
            String value = "";
            StkProduct stkProduct = stockNameQj2Product.get(name);
            if (stkProduct != null) {
                value = stkProduct.name;
            }
            acAutomaton.getKey2link().put(name, value);
        });
  • 集合--》map
List<ImageModel> imageModelList = null;
Map<Long, String> imagesMap = null;
imagesMap = imageModelList.stream().collect(Collectors.toMap(ImageModel::getAid, o -> IMAGE_ADDRESS_PREFIX + o.getUrl()));
              
             

Map<String, String> kvMap = postDetailCacheList.stream().collect(Collectors.toMap((detailCache) ->
                getBbsSimplePostKey(detailCache.getTid()), JSON::toJSONString));


Map<Long, Long> pidToTid;
List<String> pidKeyList = pidToTid.entrySet().stream().map((o) -> getKeyBbsReplyPid(o.getValue(), o.getKey())).collect(Collectors.toList());
  • DO模型---》Model模型
List<AdDO> adDOList;
adDOList.stream().map(adDo -> convertAdModel(adDo))
                .collect(Collectors.toList());
  • phones 是一個List<String>,將相同的元素分組、歸類
List<String> phones=new ArrayList<String>();
        phones.add("a");
        phones.add("b");
        phones.add("a");
        phones.add("a");
        phones.add("c");
        phones.add("b");
        Map<String, List<String>> phoneClassify = phones.stream().collect(Collectors.groupingBy(item -> item));
        System.out.println(phoneClassify);
返回結果:
{a=[a, a, a], b=[b, b], c=[c]}

參考資料

  • Lambda 表示式的 10 個示例
  • learn-java8
  • java8-tutorial
  • 一文讓你明白lambda用法與原始碼分析
  • http://blog.csdn.net/renfufei/article/details/24600507
  • http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html
  • Java8 Lambda表示式教程 https://blog.csdn.net/ioriogami/article/details/12782141
  • Java8 6個問題 https://wizardforcel.gitbooks.io/java8-tutorials/content/Java%208%20%E7%9A%846%E4%B8%AA%E9%97%AE%E9%A2%98.html

最全的Java後端知識體系 https://www.pdai.tech, 每天更新中...。