lambda函數(shù)式編程詳解

概念

Lambda 表達(dá)式,也可稱為閉包,它是推動(dòng) Java 8 發(fā)布的最重要新特性,使用 Lambda 表達(dá)式可以使代碼變的更加簡(jiǎn)潔緊湊,Lambda 允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。
函數(shù)式接口是Java8新增加的內(nèi)容。如果一個(gè)接口只有一個(gè)抽象方法,那么該接口就是函數(shù)式接口。

語(yǔ)法

1.(parameters) -> expression
2.(parameters) ->{ statements; }
3.()->expression

特性

1.可選類型聲明:不需要聲明參數(shù)類型,編譯器可以統(tǒng)一識(shí)別參數(shù)值
2.可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無(wú)需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)。
3.可選的大括號(hào):如果主體包含了一個(gè)語(yǔ)句,就不需要使用大括號(hào)。
4.可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值,大括號(hào)需要指定明表達(dá)式返回了一個(gè)數(shù)值。

   // 類型聲明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用類型聲明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括號(hào)中的返回語(yǔ)句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 沒(méi)有大括號(hào)及返回語(yǔ)句
      MathOperation division = (int a, int b) -> a / b;

   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }

注意

1.Lambda 表達(dá)式主要用來(lái)定義行內(nèi)執(zhí)行的方法類型接口,例如,一個(gè)簡(jiǎn)單方法接口。在上面例子中,我們使用各種類型的Lambda表達(dá)式來(lái)定義MathOperation接口的方法。然后我們定義了sayMessage的執(zhí)行。
2.Lambda 表達(dá)式免去了使用匿名方法的麻煩,并且給予Java簡(jiǎn)單但是強(qiáng)大的函數(shù)化的編程能力。
3.在 Lambda 表達(dá)式當(dāng)中不允許聲明一個(gè)與局部變量同名的參數(shù)或者局部變量

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //編譯會(huì)出錯(cuò)

4.lambda 表達(dá)式的局部變量可以不用聲明為 final,但是必須不可被后面的代碼修改(即隱性的具有 final 的語(yǔ)義)

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//報(bào)錯(cuò)信息:Local variable num defined in an enclosing scope must be final or effectively 
 final

5.lambda 表達(dá)式只能引用標(biāo)記了 final 的外層局部變量,這就是說(shuō)不能在 lambda 內(nèi)部修改定義在域外的局部變量,否則會(huì)編譯錯(cuò)誤。

   final static String salutation = "Hello! ";
   
   public static void main(String args[]){
      GreetingService greetService1 = message -> 
      System.out.println(salutation + message);
      greetService1.sayMessage("Runoob");
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }

函數(shù)式編程的特性與優(yōu)缺點(diǎn)

1、函數(shù)是"第一等公民"
什么是"第一等公民"?所謂"第一等公民"(first class),指的是函數(shù)與其他數(shù)據(jù)類型一樣,處于平等地位,它不僅擁有一切傳統(tǒng)函數(shù)的使用方式(聲明和調(diào)用),可以賦值給其他變量(賦值),也可以作為參數(shù),傳入另一個(gè)函數(shù)(傳參),或者作為別的函數(shù)的返回值(返回)。函數(shù)可以作為參數(shù)進(jìn)行傳遞,意味我們可以把行為"參數(shù)化",處理邏輯可以從外部傳入,這樣程序就可以設(shè)計(jì)得更靈活。
2、沒(méi)有"副作用"
所謂"副作用"(side effect),指的是函數(shù)內(nèi)部與外部互動(dòng)(最典型的情況,就是修改全局變量的值),產(chǎn)生運(yùn)算以外的其他結(jié)果。函數(shù)式編程強(qiáng)調(diào)沒(méi)有"副作用",意味著函數(shù)要保持獨(dú)立,所有功能就是返回一個(gè)新的值,沒(méi)有其他行為,尤其是不得修改外部變量的值。
3、引用透明
引用透明(Referential transparency),指的是函數(shù)的運(yùn)行不依賴于外部變量或"狀態(tài)",只依賴于輸入的參數(shù),任何時(shí)候只要參數(shù)相同,引用函數(shù)所得到的返回值總是相同的。這里強(qiáng)調(diào)了一點(diǎn)"輸入"不變則"輸出"也不變,就像數(shù)學(xué)函數(shù)里面的f(x),只要輸入的x一樣那得到的結(jié)果也肯定定是一樣的。

優(yōu)點(diǎn)

1、代碼簡(jiǎn)潔,開發(fā)快速。
函數(shù)式編程大量使用函數(shù),減少了代碼的重復(fù),因此程序比較短,開發(fā)速度較快。Paul Graham在《黑客與畫家》一書中寫道:同樣功能的程序,極端情況下,Lisp代碼的長(zhǎng)度可能是C代碼的二十分之一。如果程序員每天所寫的代碼行數(shù)基本相同,這就意味著,"C語(yǔ)言需要一年時(shí)間完成開發(fā)某個(gè)功能,Lisp語(yǔ)言只需要不到三星期。反過(guò)來(lái)說(shuō),如果某個(gè)新功能,Lisp語(yǔ)言完成開發(fā)需要三個(gè)月,C語(yǔ)言需要寫五年。"當(dāng)然,這樣的對(duì)比故意夸大了差異,但是"在一個(gè)高度競(jìng)爭(zhēng)的市場(chǎng)中,即使開發(fā)速度只相差兩三倍,也足以使得你永遠(yuǎn)處在落后的位置。"

  1. 接近自然語(yǔ)言,易于理解
Map<String,List<Student>> studentsMap = students.stream().collect(Collectors.groupingBy(Student::getSex));
  1. 更方便的代碼管理
    函數(shù)式編程不依賴、也不會(huì)改變外界的狀態(tài),只要給定輸入?yún)?shù),返回的結(jié)果必定相同。因此,每一個(gè)函數(shù)都可以被看做獨(dú)立單元,很有利于進(jìn)行單元測(cè)試(unit testing)和除錯(cuò)(debugging),以及模塊化組合
  2. 易于"并發(fā)編程"
    函數(shù)式編程不需要考慮"死鎖"(deadlock),因?yàn)樗恍薷淖兞浚愿静淮嬖?鎖"線程的問(wèn)題。不必?fù)?dān)心一個(gè)線程的數(shù)據(jù),被另一個(gè)線程修改,所以可以很放心地把工作分?jǐn)偟蕉鄠€(gè)線程,部署"并發(fā)編程"(concurrency)。
    請(qǐng)看下面的代碼:
    var s1 = Op1();
    var s2 = Op2();
    var s3 = concat(s1, s2);
    由于s1和s2互不干擾,不會(huì)修改變量,誰(shuí)先執(zhí)行是無(wú)所謂的,所以可以放心地增加線程,把它們分配在兩個(gè)線程上完成。其他類型的語(yǔ)言就做不到這一點(diǎn),因?yàn)閟1可能會(huì)修改系統(tǒng)狀態(tài),而s2可能會(huì)用到這些狀態(tài),所以必須保證s2在s1之后運(yùn)行,自然也就不能部署到其他線程上了。多核CPU是將來(lái)的潮流,所以函數(shù)式編程的這個(gè)特性非常重要。
  3. 代碼的熱升級(jí)
    函數(shù)式編程沒(méi)有副作用,只要保證接口不變,內(nèi)部實(shí)現(xiàn)是外部無(wú)關(guān)的。所以,可以在運(yùn)行狀態(tài)下直接升級(jí)代碼,不需要重啟,也不需要停機(jī)。Erlang語(yǔ)言早就證明了這一點(diǎn),它是瑞典愛(ài)立信公司為了管理電話系統(tǒng)而開發(fā)的,電話系統(tǒng)的升級(jí)當(dāng)然是不能停機(jī)的。

lambda 優(yōu)缺點(diǎn)嗎???

1、函數(shù)式編程常被認(rèn)為嚴(yán)重耗費(fèi)在CPU和存儲(chǔ)器資源。主因有二:

  • 早期的函數(shù)式編程語(yǔ)言實(shí)現(xiàn)時(shí)并無(wú)考慮過(guò)效率問(wèn)題。

  • 有些非函數(shù)式編程語(yǔ)言為求提升速度,不提供自動(dòng)邊界檢查或自動(dòng)垃圾回收等功能。

    惰性求值亦為語(yǔ)言如Haskell增加了額外的管理工作。
    2、語(yǔ)言學(xué)習(xí)曲線陡峭,難度高
    函數(shù)式語(yǔ)言對(duì)開發(fā)者的要求比較高,學(xué)習(xí)曲線比較陡,而且很容易因?yàn)槠潇`活的語(yǔ)法控制不好程序的結(jié)構(gòu)。

編程世界我來(lái)啦

1.我們?cè)倏匆幌翴ntBinaryOperator的定義

@FunctionalInterface
public interface IntBinaryOperator {
    /**
     * Applies this operator to the given operands.
     * @param left the first operand
     * @param right the second operand
     * @return the operator result
     */
    int applyAsInt(int left, int right);
}

我們得知IntBinaryOperator是一個(gè)接口并且上面有一個(gè)@FunctionalInterface的注解,@FunctionalInterface標(biāo)注了這是一個(gè)函數(shù)式接口,所以我們知道了(int a, int b) -> {return a + b;}返回的一個(gè)IntBinaryOperator的匿名實(shí)現(xiàn)類。

2.在java 8中已經(jīng)為我們定義了很多常用的函數(shù)式接口它們都放在java.util.function包下面,一般有以下常用的四大核心接口:

Consumer<T>(消費(fèi)型接口) T void 對(duì)類型為T的對(duì)象應(yīng)用操作。void accept(T t)
Supplier<T>(供給型接口) 無(wú) T 返回類型為T的對(duì)象。 T get();
Function<T, R>(函數(shù)型接口) T R 對(duì)類型為T的對(duì)象應(yīng)用操作并返回R類型的對(duì)象R apply(T t);
Predicate<T>(斷言型接口) T boolean 確定類型為T的對(duì)象是否滿足約束。boolean test(T t);

以下是擴(kuò)展 或者 增強(qiáng)上邊的接口

UnaryOperator<T>:繼承自Function<T, T>,接受一個(gè)參數(shù)T,返回相同類型T的結(jié)果
BiFunction<T, U, R>:接受兩個(gè)參數(shù)T和U,返回結(jié)果R
BinaryOperator<T>:繼承自BiFunction<T, T, T>,接受兩個(gè)相同類型T的參數(shù),返回相同類型T的結(jié)果
Runnable:實(shí)際上是不接受任何參數(shù),也不返回結(jié)果
Comparable<T>:實(shí)際上是接受兩個(gè)相同類型T的參數(shù),返回int
Callable<V>:不接受任何參數(shù),返回結(jié)果V

Consumer 應(yīng)用的例子:

 /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
// 應(yīng)用
  Consumer t = (c)-> {System.out.println(c);};
        t.accept("v");

default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
Consumer<Integer> cons = (x)->{
    System.out.println("第一個(gè)Consumer"+(++x)); // 51
};      
Consumer<Integer> cons1 = (x)->{
    System.out.println("第二個(gè)Consumer"+(--x)); // 49
};
cons.andThen(cons1).accept(50);
BiConsumer<Integer,Integer> bicons = (x,y)->{
    System.out.println("第一個(gè)biConsumer:"+(x+y));
};

BiConsumer<Integer,Integer> bicons1 = (x,y)->{
    System.out.println("第二個(gè)biConsumer:"+x*y);
};
// 第一個(gè)biConsumer:8
bicons.accept(3, 5);
// 第一個(gè)biConsumer:8
// 第二個(gè)biConsumer:15     
bicons.andThen(bicons1).accept(3, 5);

Supplier 例子


 Supplier gs = () ->{return "b";};
        gs.get();

Function 應(yīng)用例子

   /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
/ myFun就是上面定義的myFUn
Integer applyResult = myFun.apply(1); // applyResult =2
Function<Integer,Integer> myFun = (x)->{
     return x+1;
};
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}
Function<Integer,Integer> plus = (x)->{
    return x+1;
};

Function<Integer,Integer> multip = (x)->{
    return x*3;
};
Integer apply = plus.compose(multip).apply(2); // 此處執(zhí)行的就是2*3+1=7
// 兩個(gè)Funtion的調(diào)用反過(guò)來(lái)一下
Integer overApply = multip.compose(plus).apply(2); // 此處執(zhí)行的就是(2+1)*3=9
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}
Function<Integer,Integer> plus = (x)->{
    return x+1;
};

Function<Integer,Integer> multip = (x)->{
    return x*3;
};
Integer apply = plus.andThen(multip).apply(2); // 此處執(zhí)行的就是(2+1)*3=9
// 兩個(gè)Funtion的調(diào)用反過(guò)來(lái)一下,跟上面的一樣
Integer overApply = multip.andThen(plus).apply(2); // 此處執(zhí)行的就是2*3+1=7
 /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
Function<Integer, Integer> identity = Function.identity();
Integer apply = identity.apply(5); // apply=5
BiFunction<Integer,Integer,Integer> plus = (x,y)->{
    return x+y;
};

BiFunction<Integer,Integer,Integer> multip = (x,y)->{
     return x*y;
};

Integer apply = plus.apply(3, 2); // 3+2=5
Integer apply1 = multip.apply(3, 2); // 3*2=6
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
BiFunction<Integer, Integer, Integer> multip = (x, y) -> {
    return x * y;
};
Function<Integer, Integer> sqr = (x) -> {
    return x * x;
};   
Integer apply = multip.andThen(sqr).apply(3, 5); //(3*5)*(3*5)= 225
//他是這么執(zhí)行的,先執(zhí)行2個(gè)參數(shù)的就是上面的multip 方法,然后得到一個(gè)結(jié)果,再將結(jié)果當(dāng)做參數(shù)去執(zhí)行只有一個(gè)參數(shù)的sqr方法。所以難怪沒(méi)有反過(guò)來(lái)執(zhí)行的compose方法了,因?yàn)椴荒芡瑫r(shí)得到2個(gè)返回值,所以只能先執(zhí)行BiFunction方法了。

Predicate 斷言例子 適合 集合操作的過(guò)濾


    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
Predicate<String> pre = (x)->{
    return x.equals("hello word!");
};
boolean test = pre.test("hello world!"); // test = false
default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
Predicate<Integer> preInt = (x)->{
    return x>0;
};  
Predicate<Number> preNum = (x)->{
    return x.equals(10);
};
preNum.and(preInt).test(10); //編譯報(bào)錯(cuò)
boolean test = preInt.and(preNum).test(10); //true

default Predicate<T> negate() {
        return (t) -> !test(t);
    }
Predicate<Integer> preInt = (x)->{
    return x>0;
};  
Predicate<Number> preNum = (x)->{
    return x.equals(10);
};
boolean test = preInt.or(preNum).test(5); //true

Predicate<Integer> preInt = (x)->{
    return x>0;
};
boolean test = preInt.negate().test(5); // false 非操作

跟Function一樣,Predicate也有傳2個(gè)參數(shù)的BiPredicate接口

BiPredicate<Integer,Integer> biprePlus = (x,y)->{
    return x+y>10;
};
BiPredicate<Integer,Integer> bipreMutip = (x,y)->{
    return x*y>10;
};
boolean t1 = biprePlus.test(3, 5); // false
boolean t2 = biprePlus.and(bipreMutip).test(3, 5); // false
boolean t3 = biprePlus.or(bipreMutip).test(3, 5); // true
boolean t4 = biprePlus.negate().test(3, 5); // true

lambda方法引用

1.類名::靜態(tài)方法名

如果函數(shù)式接口的抽象方法的實(shí)現(xiàn)剛好可以通過(guò)調(diào)用一個(gè)靜態(tài)方法來(lái)實(shí)現(xiàn),那么就可以使用該類型的方法引用。例如,一個(gè)Integer類型集合,現(xiàn)在需要把它轉(zhuǎn)換成對(duì)應(yīng)的String類型的集合,就可以使用下面的代碼

List<Integer> list = Arrays.asList(1, 2, 3, 4);
List<String> strList = list.stream()
                        .map(String::valueOf)
                        .collect(Collectors.toList());
2. 對(duì)象名::實(shí)例方法名
public class Student {

    private String name;

    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public int compareByScore(Student student1, Student student2) {
        return student1.getScore() - student2.getScore();
    }

    public int compareByName(Student student1, Student student2) {
        return student1.getName()
                .compareToIgnoreCase(student2.getName());
    }
}
Student student1 = new Student("zhangsan", 10);
Student student2 = new Student("lisi", 90);
Student student3 = new Student("wangwu", 50);
Student student4 = new Student("zhaoliu", 40);

List<Student> students = Arrays.asList(student1, student2, student3, student4);
students.sort(student1::compareByName);
3. 類名::實(shí)例方法名

 public int compareByName(Student student) {
        return this.getName()
        .compareToIgnoreCase(student.getName());
    }
Student student1 = new Student("zhangsan", 10);
Student student2 = new Student("lisi", 90);
Student student3 = new Student("wangwu", 50);
Student student4 = new Student("zhaoliu", 40);

List<Student> students = Arrays.asList(student1, student2, student3, student4);
students.sort(Student::compareByName);
4. 構(gòu)造方法引用: 類名::new
public class MethodReferenceTest {

    public String getString(Supplier<String> supplier) {
        return supplier.get() + "test";
    }

    public static void main(String[] args) {
        MethodReferenceTest test = new MethodReferenceTest();
        System.out.println(test.getString(String::new));
    }
}

1.中間操作:

結(jié)合Predicate接口,F(xiàn)ilter對(duì)流對(duì)象中的所有元素進(jìn)行過(guò)濾,該操作是一個(gè)中間操作,這意味著你可以在操作返回結(jié)果的基礎(chǔ)上進(jìn)行其他操作

public static void sreamFilterTest(List<String> lists){ //要明確這list的泛型類型,否則jvm不能根據(jù)上下文確定參數(shù)類型
        lists.stream().filter((s -> s.startsWith("a"))).forEach(System.out::println);//將開頭是a的過(guò)濾出來(lái)

        //等價(jià)于以上操作
        Predicate<String> predicate = (s) -> s.startsWith("a");//將開頭是a的過(guò)濾出來(lái)
        lists.stream().filter(predicate).forEach(System.out::println);

        //連續(xù)過(guò)濾
        Predicate<String> predicate1 = (s -> s.endsWith("1"));//將開頭是a,并且結(jié)尾是1的過(guò)濾出來(lái)
        lists.stream().filter(predicate).filter(predicate1).forEach(System.out::println);
    }
排序(sorted)

結(jié)合Comparator,該操作返回一個(gè)排序過(guò)后的流的視圖,原始流的順序不會(huì)改變。通過(guò)Comparator來(lái)指定排序規(guī)則,默認(rèn)是自然排序

 private static void streamSortedTest(List<String> list){
        //默認(rèn)排序
        list.stream().filter(s -> s.startsWith("a")).forEach(System.out::println);
        System.out.println("- - - - - - - - -");
        //自定義排序
        list.stream().sorted(((s, t1) -> t1.compareTo(s))).filter(s -> s.startsWith("a")).forEach(System.out::println);
    }

映射(map)

private static void streamMapTest(List<String> list){
        list.stream().map(String::toUpperCase).sorted((s, t1) -> t1.compareTo(s)).forEach(System.out::println);
        System.out.println("- - - - - - ");
        //自定義映射規(guī)則
        Function<String,String> function = s -> {return  s + ".map3";};
        list.stream().map(function).forEach(System.out::println);
    }

完結(jié)操作方法

匹配(match)

用來(lái)判斷某個(gè)predicate是否和流對(duì)象相匹配,最終返回boolean類型的結(jié)果

 private static void streamMatchTest(List<String> list){
        //流對(duì)象中只要有一個(gè)元素匹配就返回true
        boolean anyStartWithA = list.stream().anyMatch(s -> s.startsWith("a"));
        System.out.println("集合中是否有以'a'來(lái)頭:"+ anyStartWithA);
        //流對(duì)象中每一個(gè)元素都匹配才返回true
        boolean allStartWithA = list.stream().allMatch(s -> s.startsWith("a"));
        System.out.println("集合中每一個(gè)都是以'a'開頭:"+ allStartWithA);
        //流對(duì)象中沒(méi)有匹配時(shí)返回true
        boolean noneStartWithA = list.stream().noneMatch(s -> s.startsWith("c"));
        System.out.println("集合中沒(méi)有以'c'開頭:"+ noneStartWithA);
    }

收集(collect)

在對(duì)經(jīng)過(guò)變換后,將變換的stream元素收集,比如將這些元素存在集合中,可以使用stream提供的collect方法

private static void streamCollectTest(List<String> list){
        List<String> listNew = list.stream().filter(s -> s.startsWith("b")).sorted().collect(Collectors.toList());
        System.out.println(listNew );
    }

規(guī)約(reduce)

允許我們用自己的方式計(jì)算元素或者將一個(gè)stream中元素以某種規(guī)律關(guān)聯(lián)

private static void streamReduceTest(List<String> list){
        Optional<String> optional = list.stream().sorted().reduce((s, s2) -> {
            System.out.println(s+"-"+s2);
            return s+"-"+s2;
        });
    }

計(jì)數(shù)(count)

用來(lái)統(tǒng)計(jì)流中元素的總數(shù)

private static void streamCountTest(List<String> list){
        long count = list.stream().filter(s -> s.startsWith("b")).count();
        System.out.println("以'b'開頭的數(shù)量:"+ count);
    }

并行操作stream

并行Stream:基于Fork-join并行分解框架實(shí)現(xiàn),將大數(shù)據(jù)集合切分為多個(gè)小數(shù)據(jù)結(jié)合交給不同的線程去處理,這樣在多核處理情況下,性能會(huì)得到很大的提高。
這和MapReduce的設(shè)計(jì)理念一致:大任務(wù)化小,小任務(wù)再分配到不同的機(jī)器執(zhí)行。只不過(guò)這里的小任務(wù)是交給不同的處理器。
結(jié)果是性能提高50%,單核下還是串行流性能比較好,并行流的使用場(chǎng)景是多核+大數(shù)據(jù)

 //創(chuàng)建一個(gè)大集合
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            UUID uuid = UUID.randomUUID();
            list.add(uuid.toString());
        }
//并行stream
    private static void parallelStreamSortedTest(List<String> list){
        long startTime = System.nanoTime();//返回最準(zhǔn)確的可用系統(tǒng)計(jì)時(shí)器的當(dāng)前值,以毫微秒為單位。
        long count = list.parallelStream().sorted().count();
        long endTime = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
        System.out.printf("并行排序花費(fèi)時(shí)間:%d ms",millis);
    }
    //串行stream
    private static void streamSortedTest(List<String> list){
        long startTime = System.nanoTime();//返回最準(zhǔn)確的可用系統(tǒng)計(jì)時(shí)器的當(dāng)前值,以毫微秒為單位。
        long count = list.stream().sorted().count();
        long endTime = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
        System.out.printf("串行排序花費(fèi)時(shí)間:%d ms",millis);
    }

高階玩法

1.傳入數(shù)組ids,在list<Obj>上操作,找出Obj中id想匹配的,并且按照id進(jìn)行collect成map(這里假設(shè)找出來(lái)的按照id不重復(fù)

public Map<Integer, MyObj> getOperationByShipmentIds(Collection<Integer> ids) {
 return storage
   .stream()
   .filter(op -> ids.contains(op.getId()))
   .collect(Collectors.toMap(MyObj::getId, Function.identity()));
}
// 接上面的,假設(shè)id可以重復(fù)
public Map<Integer, MyObj> getOperationByShipmentIds(Collection<Integer> ids) {
 return storage
   .stream()
   .filter(op -> ids.contains(op.getId()))
   .collect(Collectors.groupingBy(MyObj::getId));
}
// 對(duì)象列表某一列求和
list.values().stream().mapToInt(obj -> obj.getIntField()).sum();
//多個(gè)list追加到同一個(gè)中
List<MyObject> list = services.stream()
        .flatMap(s -> s.getObjects().stream())
        .collect(Collectors.toList());
// 計(jì)算List中的元素的最大值,最小值,總和及平均值
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());
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容