最全最強 Java 8 - 函數編程(lambda表達式)

Java 8 - 函數編程(lambda表達式)

咱們關心的是如何寫出好代碼,而不是符合函數編程風格的代碼。 @pdaihtml

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

簡介

在Java世界裏面,面向對象仍是主流思想,對於習慣了面向對象編程的開發者來講,抽象的概念並不陌生。面向對象編程是對數據進行抽象,而函數式編程是對行爲進行抽象。現實世界中,數據和行爲並存,程序也是如此,所以這兩種編程方式咱們都得學。git

這種新的抽象方式還有其餘好處。不少人不老是在編寫性能優先的代碼,對於這些人來講,函數式編程帶來的好處尤其明顯。程序員能編寫出更容易閱讀的代碼——這種代碼更多地表達了業務邏輯,而不是從機制上如何實現。易讀的代碼也易於維護、更可靠、更不容易出錯。程序員

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

面向對象編程是對數據進行抽象;函數式編程是對行爲進行抽象。web

核心思想:使用不可變值和函數,函數對一個值進行處理,映射成另外一個值。正則表達式

對核心類庫的改進主要包括集合類的API和新引入的流Stream。流使程序員能夠站在更高的抽象層次上對集合進行操做。express

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("bob@gmail.com", "alice@hotmail.com")
    .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]}

參考資料

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

相關文章
相關標籤/搜索