成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java8新特性學(xué)習(xí)筆記

wthee / 972人閱讀

摘要:雖然目前工作環(huán)境仍然以為主,不過(guò)目前已是大勢(shì)所趨了。標(biāo)準(zhǔn)函數(shù)式接口新的包定義旨在使用的廣泛函數(shù)式接口。這一改進(jìn)使得擁有了類似于多繼承的能力。

從Java8發(fā)布到現(xiàn)在有好幾年了,而Java9也提上發(fā)布日程了(沒(méi)記錯(cuò)的話好像就是這個(gè)月2017年7月,也許會(huì)再度跳票吧,不過(guò)沒(méi)關(guān)系,穩(wěn)定大于一切,穩(wěn)定了再發(fā)布也行),現(xiàn)在才開(kāi)始去真正學(xué)習(xí),說(shuō)來(lái)也是慚愧。雖然目前工作環(huán)境仍然以Java6為主,不過(guò)Java8目前已是大勢(shì)所趨了。Java8帶來(lái)了許多令人激動(dòng)的新特性,如lambda表達(dá)式,StreamsAPI與并行集合計(jì)算,新的時(shí)間日期API(借鑒joda-time),字節(jié)碼支持保存方法參數(shù)名(對(duì)于框架開(kāi)發(fā)真的是非常贊的一個(gè)特性),Optional類解決空指針問(wèn)題(雖然Guava中早就有這個(gè)了)等。

lambda表達(dá)式

語(yǔ)法:

v->System.out.println(v)

(v)->System.out.println(v)

(String v)->System.out.println(v)

(v)->{System.out.println(v);return v+1;}

List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.forEach((Integer value) -> System.out.println(value));

注意:lambda表達(dá)式內(nèi)如果引用了外部的局部變量,那么這個(gè)局部變量必須是final的,如果不是,編譯器會(huì)自動(dòng)給加上final。比如下面這段是錯(cuò)誤的

//這是錯(cuò)誤的
int num = 2;
Function stringConverter = (from) -> from * num;
num++;//會(huì)報(bào)錯(cuò),因?yàn)榇藭r(shí)num已經(jīng)是final聲明的了
System.out.println(stringConverter.apply(3));
函數(shù)式接口:

僅有一個(gè)抽象方法的接口(注:Java8之后接口也可以有非抽象方法,所以此處強(qiáng)調(diào)只有一個(gè)抽象方法的接口)

可選注解:@FunctionalInterface , 作用:編譯器會(huì)檢查,javadoc文檔中會(huì)特別說(shuō)明。

這里需要強(qiáng)調(diào)的是,函數(shù)式接口只能有一個(gè)抽象方法,而不是指只能有一個(gè)方法。這分兩點(diǎn)來(lái)說(shuō)明。首先,在Java 8中,接口運(yùn)行存在實(shí)例方法(見(jiàn)默認(rèn)方法一節(jié)),其次任何被java.lang.Object實(shí)現(xiàn)的方法,都不能視為抽象方法,因此,下面的NonFunc接口不是函數(shù)式接口,因?yàn)閑quals()方法在java.lang.Object中已經(jīng)實(shí)現(xiàn)。

標(biāo)準(zhǔn)函數(shù)式接口
新的 java.util.function 包定義旨在使用 lambdas 的廣泛函數(shù)式接口。這些接口分為幾大類:

Function:接受一個(gè)參數(shù),基于參數(shù)值返回結(jié)果

Predicate:接受一個(gè)參數(shù),基于參數(shù)值返回一個(gè)布爾值

BiFunction:接受兩個(gè)參數(shù),基于參數(shù)值返回結(jié)果

Supplier:不接受參數(shù),返回一個(gè)結(jié)果

Consumer:接受一個(gè)參數(shù),無(wú)結(jié)果 (void)

interface NonFunc {
boolean equals(Object obj);
}

同理,下面實(shí)現(xiàn)的IntHandler接口符合函數(shù)式接口要求,雖然看起來(lái)它不像,但實(shí)際上它是一個(gè)完全符合規(guī)范的函數(shù)式接口。

@FunctionalInterface
public static interface IntHandler{
    void handle(int i);
    boolean equals(Object obj);
}
接口默認(rèn)方法

在Java 8之前的版本,接口只能包含抽象方法,Java 8 對(duì)接口做了進(jìn)一步的增強(qiáng)。在接口中可以添加使用 default 關(guān)鍵字修飾的非抽象方法。還可以在接口中定義靜態(tài)方法。如今,接口看上去與抽象類的功能越來(lái)越類似了。這一改進(jìn)使得Java 8擁有了類似于多繼承的能力。一個(gè)對(duì)象實(shí)例,將擁有來(lái)自于多個(gè)不同接口的實(shí)例方法。
比如,對(duì)于接口IHorse,實(shí)現(xiàn)如下:

public interface IHorse{
    void eat();
    default void run(){
        System.out.println(“hourse run”);
    }
}

在Java 8中,使用default關(guān)鍵字,可以在接口內(nèi)定義實(shí)例方法。注意,這個(gè)方法是并非抽象方法,而是擁有特定邏輯的具體實(shí)例方法。

所有的動(dòng)物都能自由呼吸,所以,這里可以再定義一個(gè)IAnimal接口,它也包含一個(gè)默認(rèn)方法breath()。

public interface IAnimal {
  default void breath(){
      System.out.println(“breath”);
    }
}

騾是馬和驢的雜交物種,因此騾(Mule)可以實(shí)現(xiàn)為IHorse,同時(shí)騾也是動(dòng)物,因此有:

public class Mule implements IHorse,IAnimal{
    @Override
    public void eat() {
        System.out.println(“Mule eat”);
      }
    public static void main(String[] args) {
     Mule m=new Mule();
     m.run();
     m.breath();
      }
}

注意上述代碼中Mule實(shí)例同時(shí)擁有來(lái)自不同接口的實(shí)現(xiàn)方法。在這Java 8之前是做不到的。從某種程度上說(shuō),這種模式可以彌補(bǔ)Java單一繼承的一些不便。但同時(shí)也要知道,它也將遇到和多繼承相同的問(wèn)題,如果IDonkey也存在一個(gè)默認(rèn)的run()方法,那么同時(shí)實(shí)現(xiàn)它們的Mule,就會(huì)不知所措,因?yàn)樗恢缿?yīng)該以哪個(gè)方法為準(zhǔn)。此時(shí),由于IHorse和IDonkey擁有相同的默認(rèn)實(shí)例方法,故編譯器會(huì)拋出一個(gè)錯(cuò)誤:Duplicate default methods named run with the parameters () and () are inherited from the types IDonkey and IHorse

接口默認(rèn)實(shí)現(xiàn)對(duì)于整個(gè)函數(shù)式編程的流式表達(dá)式非常重要。比如,大家熟悉的java.util.Comparator接口,它在JDK 1.2時(shí)就已經(jīng)被引入。在Java 8中,Comparator接口新增了若干個(gè)默認(rèn)方法,用于多個(gè)比較器的整合。其中一個(gè)常用的默認(rèn)如下:

default Comparator thenComparing(Comparator other) {
  Objects.requireNonNull(other);
  return (Comparator & Serializable) (c1, c2) -> {
    int res = compare(c1, c2);
    return (res != 0) ? res : other.compare(c1, c2);
  };

}

有個(gè)這個(gè)默認(rèn)方法,在進(jìn)行排序時(shí),我們就可以非常方便得進(jìn)行元素的多條件排序,比如,如下代碼構(gòu)造一個(gè)比較器,它先按照字符串長(zhǎng)度排序,繼而按照大小寫不敏感的字母順序排序。

Comparator cmp = Comparator.comparingInt(String::length)
.thenComparing(String.CASE_INSENSITIVE_ORDER);
接口靜態(tài)方法:

在接口中,還允許定義靜態(tài)的方法。接口中的靜態(tài)方法可以直接用接口來(lái)調(diào)用。
例如,下面接口中定義了一個(gè)靜態(tài)方法 find,該方法可以直接用 StaticFunInterface .find() 來(lái)調(diào)用。

public interface StaticFunInterface {
public static int find(){
return 1;
}
}
public class TestStaticFun {
public static void main(String[] args){
//接口中定義了靜態(tài)方法 find 直接被調(diào)用
StaticFunInterface.fine();
}
}

說(shuō)明:雖然我知道了default方法是為了便于集合接口(新的StreamsAPI)向后兼容而設(shè)計(jì)的,但是這個(gè)接口靜態(tài)方法暫時(shí)還沒(méi)體會(huì)其作用,可能得接觸多了才會(huì)明白吧。

方法引用

靜態(tài)方法引用:ClassName::methodName

實(shí)例上的實(shí)例方法引用:instanceReference::methodName (這里還可以使用this)

超類上的實(shí)例方法引用:super::methodName

類型上的實(shí)例方法引用:ClassName::methodName

構(gòu)造方法引用:Class::new

數(shù)組構(gòu)造方法引用:TypeName[]::new

public class InstanceMethodRef {
  public static void main(String[] args) {
    List users=new ArrayList();
    for(int i=1;i<10;i++){
      users.add(new User(i,”billy”+Integer.toString(i)));
    }
    users.stream().map(User::getName).forEach(System.out::println);
  }
}

注意幾點(diǎn):
對(duì)于第一個(gè)方法引用User::getName,表示User類的實(shí)例方法。在執(zhí)行時(shí),Java會(huì)自動(dòng)識(shí)別流中的元素(這里指User實(shí)例)是作為調(diào)用目標(biāo)還是調(diào)用方法的參數(shù)。
一般來(lái)說(shuō),如果使用的是靜態(tài)方法,或者調(diào)用目標(biāo)明確,那么流內(nèi)的元素會(huì)自動(dòng)作為參數(shù)使用。如果函數(shù)引用表示實(shí)例方法,并且不存在調(diào)用目標(biāo),那么流內(nèi)元素就會(huì)自動(dòng)作為調(diào)用目標(biāo)。
因此,如果一個(gè)類中存在同名的實(shí)例方法和靜態(tài)函數(shù),那么編譯器就會(huì)感到很困惑,因?yàn)榇藭r(shí),它不知道應(yīng)該使用哪個(gè)方法進(jìn)行調(diào)用。

Stream API

打開(kāi) Collection Api可以看到多了一個(gè) stream() default 方法:

default Stream stream() {
    return StreamSupport.stream(spliterator(), false);
}

Stream 允許以聲明方式處理集合等可以轉(zhuǎn)換為 Stream 的數(shù)據(jù), 他有很多特點(diǎn):

內(nèi)部迭代 :與原有的 Iterator 不同, Stream 將迭代操作(類似 for / for-each )全部固化到了Api內(nèi)部實(shí)現(xiàn), 用戶只需傳入表達(dá)計(jì)算邏輯的 lambda 表達(dá)式(可以理解為 Supplier 、 Function 這些的 @FunctionalInterface 的實(shí)現(xiàn)), Stream 便會(huì)自動(dòng)迭代- 數(shù)據(jù)觸發(fā)計(jì)算邏輯并生成結(jié)果. 內(nèi)部迭代主要解決了兩方面的問(wèn)題: 避免集合處理時(shí)的套路和晦澀 ; 便于庫(kù)內(nèi)部實(shí)現(xiàn)的多核并行優(yōu)化 .

流水線 :很多 Stream 操作會(huì)再返回一個(gè) Stream , 這樣多個(gè)操作就可以鏈接起來(lái), 形成一個(gè)大的流水線, 使其看起來(lái)像是 對(duì)數(shù)據(jù)源進(jìn)行數(shù)據(jù)庫(kù)式查詢 , 這也就讓自動(dòng)優(yōu)化成為可能, 如 隱式并行 .

隱式并行 :如將 .stream() 替換為 .parallelStream() , Stream 則會(huì)自動(dòng)啟用Fork/Join框架, 并行執(zhí)行各條流水線, 并最終自動(dòng)將結(jié)果進(jìn)行合并.

延遲計(jì)算 :由于 Stream 大部分的操作(如 filter() 、 generate() 、 map() …)都是接受一段 lambda 表達(dá)式, 邏輯類似接口實(shí)現(xiàn)(可以看成是 回調(diào) ), 因此代碼并不是立即執(zhí)行的, 除非流水線上觸發(fā)一個(gè)終端操作, 否則中間操作不會(huì)執(zhí)行任何處理.

短路求值 :有些操作不需要處理整個(gè)流就能夠拿到結(jié)果, 很多像 anyMatch() 、 allMatch() 、 limit() , 只要找到一個(gè)元素他們的工作就可以結(jié)束, 也就沒(méi)有必要執(zhí)行后面的操作, 因此如果后面有大量耗時(shí)的操作, 此舉可大大節(jié)省性能.

Stream 構(gòu)成
一個(gè)流管道(Stream pipeline)通常由3部分構(gòu)成: 數(shù)據(jù)源(Source) -> 中間操作/轉(zhuǎn)換(Transforming) -> 終端操作/執(zhí)行(Operations) : Stream 由數(shù)據(jù)源生成, 經(jīng)由中間操作串聯(lián)起來(lái)的一條流水線的轉(zhuǎn)換, 最后由終端操作觸發(fā)執(zhí)行拿到結(jié)果.

1、數(shù)據(jù)源-Stream生成

除了前面介紹過(guò)的 collection.stream() , 流的生成方式多種多樣, 可簡(jiǎn)單概括為3類: 通用流 、 數(shù)值流 、 其他 , 其中以 通用流最為常用, 數(shù)值流是Java為 int 、 long 、 double 三種數(shù)值類型防 拆裝箱 成本所做的優(yōu)化:
A、通用流

Arrays.stream(T[] array)

Stream.empty()

Stream.generate(Supplier s) 返回?zé)o限無(wú)序流,其中每個(gè)元素由Supplier生成.

Stream.iterate(T seed, UnaryOperator f) 返回?zé)o限有序流。

Stream.of(T... values)

Stream.concat(Stream a, Stream b) 創(chuàng)建一個(gè)懶惰連接的流,其元素是第一個(gè)流的所有元素,后跟第二個(gè)流的所有元素.

StreamSupport.stream(Spliterator spliterator, boolean parallel) 從Spliterator創(chuàng)建一個(gè)新的順序流或并行流。.

B、數(shù)值流

Arrays.stream(Xxx[] array) Returns a sequential Int/Long/DoubleStream with the specified array as its source.

XxxStream.empty() Returns an empty sequential Int/Long/DoubleStream .

XxxStream.generate(XxxSupplier s) Returns an infinite sequential unordered stream where each element is generated by the provided Int/Long/DoubleSupplier .

XxxStream.iterate(Xxx seed, XxxUnaryOperator f) Returns an infinite sequential ordered Int/Long/DoubleStream like as Stream.iterate(T seed, UnaryOperator f)

XxxStream.of(Xxx... values) Returns a sequential ordered stream whose elements are the specified values.

XxxStream.concat(XxxStream a, XxxStream b) Creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.

Int/LongStream.range(startInclusive, endExclusive) Returns a sequential ordered Int/LongStream from startInclusive (inclusive) to endExclusive (exclusive) by an incremental step of 1.

Int/LongStream.rangeClosed(startInclusive, endInclusive) Returns a sequential ordered Int/LongStream from startInclusive (inclusive) to endInclusive (inclusive) by an incremental step of 1.

C、其他
C.1、I/O Stream

BufferedReader.lines()

C.2、File Stream

Files.lines(Path path)

Files.find(Path start, int maxDepth, BiPredicate matcher, FileVisitOption... options)

DirectoryStream newDirectoryStream(Path dir)

Files.walk(Path start, FileVisitOption... options)

C.3、Jar

JarFile.stream()

C.4、Random

Random.ints()

Random.longs()

Random.doubles()

C.5、Pattern

splitAsStream(CharSequence input) …

另外, 三種數(shù)值流之間, 以及數(shù)值流與通用流之間都可以相互轉(zhuǎn)換:

數(shù)值流轉(zhuǎn)換: doubleStream.mapToInt(DoubleToIntFunction mapper) 、 intStream.asLongStream() …

數(shù)值流轉(zhuǎn)通用流: longStream.boxed() 、 intStream.mapToObj(IntFunction mapper) …

通用流轉(zhuǎn)數(shù)值流: stream.flatMapToInt(Function mapper) 、 stream.mapToDouble(ToDoubleFunction mapper) …

中間操作-Stream轉(zhuǎn)換

所有的中間操作都會(huì)返回另一個(gè) Stream , 這讓多個(gè)操作可以鏈接起來(lái)組成中間操作鏈, 從而形成一條流水線, 因此它的特點(diǎn)就是前面提到的 延遲執(zhí)行 : 觸發(fā)流水線上觸發(fā)一個(gè)終端操作, 否則中間操作不執(zhí)行任何處理.

filter(Predicate predicate)

distinct() Returns a stream consisting of the distinct elements (according to Object.equals(Object) ) of this stream.

limit(long maxSize)

skip(long n)

sorted(Comparator comparator)

map(Function mapper) Returns a stream consisting of the results of applying the given function to the elements of this stream.

flatMap(Function> mapper) Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the - provided mapping function to each element.

peek(Consumer action) Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.

這里著重講解下 flatMap()
假設(shè)我們有這樣一個(gè)字符串list: List strs = Arrays.asList("hello", "alibaba", "world"); 如何列出里面各不相同的字符呢?

Stream> streamStream = strs.stream()  
        .map(str -> Arrays.stream(str.split("")));  

我們將 String 分解成 String[] 后再由 Arrays.stream() 將 String[] 映射成 Stream , 但這個(gè)結(jié)果是我們不想看到的: 我們明明想要的是 Stream 卻得到的是 Stream> , 他把我們想要的結(jié)果包到 Stream 里面了. 這時(shí)候就需要我們的 flatMap() 出場(chǎng)了:

Stream stringStream = strs.stream()
        .flatMap(str -> Arrays.stream(str.split("")));

flatMap() 把 Stream 中的層級(jí)結(jié)構(gòu)扁平化了, 將內(nèi)層 Stream 內(nèi)的元素抽取出來(lái), 最終新的 Stream 就沒(méi)有內(nèi)層 Stream 了.
可以簡(jiǎn)單概括為: flatMap() 方法讓你把一個(gè)流中的每個(gè)值都換成另一個(gè) Stream , 然后把所有的 Stream 連接起來(lái)成為一個(gè) Stream .

終端操作-Stream執(zhí)行

終端操作不僅擔(dān)負(fù)著觸發(fā)流水線執(zhí)行的任務(wù), 他還需要拿到流水線執(zhí)行的結(jié)果, 其結(jié)果為任何不是流的值.

count()

max(Comparator comparator)

min(Comparator comparator)

allMatch(Predicate predicate)

anyMatch(Predicate predicate)

noneMatch(Predicate predicate)

findAny()

findFirst()

reduce(BinaryOperator accumulator) Performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.

toArray()

forEach(Consumer action)

forEachOrdered(Consumer action) Performs an action for each element of this stream, in the encounter order of the stream if the stream has a defined encounter order.

collect(Collector collector) Performs a mutable reduction operation on the elements of this stream using a Collector .

像 IntStream / LongStream / DoubleStream 還提供了 average() 、 sum() 、 summaryStatistics() 這樣的操作, 拿到一個(gè)對(duì) Stream 進(jìn)行匯總了的結(jié)果.

java.util.stream.Stream 接口繼承自 java.util.stream.BaseStream 接口, 而 BaseStream 接口也提供了很多工具方法(如將串行流轉(zhuǎn)換為并行流的 parallel() 方法)供我們使用:

S onClose(Runnable closeHandler) Returns an equivalent stream with an additional close handler .

void close()

S unordered()

Iterator iterator()

Spliterator spliterator() Returns a spliterator for the elements of this stream.

S sequential()

S parallel()

boolean isParallel()

demo

簡(jiǎn)單點(diǎn)的:

static int [] arr={1,4,3,6,5,7,2,9};

public static void main(String[]args){
//Array.stream()方法返回了一個(gè)流對(duì)象。類似于集合或者數(shù)組,流對(duì)象也是一個(gè)對(duì)象的集合,它將給予我們遍歷處理流內(nèi)元素的功能
  Arrays.stream(arr).forEach((x)->System.out.println(x));
}

復(fù)雜點(diǎn)的:

public void joiningList() {
    // 生成一段[0,20)序列
    List list = IntStream.range(0, 20)
            .boxed()
            .collect(Collectors.toList());

    // 將list內(nèi)的偶數(shù)提取反向排序后聚合為一個(gè)String
    String string = list.stream()
            .filter(n -> n % 2 == 0)
            .sorted(Comparator.comparing((Integer i) -> i).reversed())
            .limit(3)
            .peek((i) -> System.out.println("remained: " + i))
            .map(String::valueOf)
            .collect(Collectors.joining());

    System.out.println(string);
}
public class StreamLambda {
    private List transactions;

    @Before
    public void setUp() {
        Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("Brian", "Cambridge");

        transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );
    }

    @Test
    public void action() {
        // 1. 打印2011年發(fā)生的所有交易, 并按交易額排序(從低到高)
        transactions.stream()
                .filter(transaction -> transaction.getYear() == 2011)
                .sorted(Comparator.comparing(Transaction::getValue))
                .forEachOrdered(System.out::println);

        // 2. 找出交易員都在哪些不同的城市工作過(guò)
        Set distinctCities = transactions.stream()
                .map(transaction -> transaction.getTrader().getCity())
                .collect(Collectors.toSet());   // or .distinct().collect(Collectors.toList())
        System.out.println(distinctCities);

        // 3. 找出所有來(lái)自于劍橋的交易員, 并按姓名排序
        Trader[] traders = transactions.stream()
                .map(Transaction::getTrader)
                .filter(trader -> trader.getCity().equals("Cambridge"))
                .distinct()
                .sorted(Comparator.comparing(Trader::getName))
                .toArray(Trader[]::new);
        System.out.println(Arrays.toString(traders));

        // 4. 返回所有交易員的姓名字符串, 并按字母順序排序
        String names = transactions.stream()
                .map(transaction -> transaction.getTrader().getName())
                .distinct()
                .sorted(Comparator.naturalOrder())
                .reduce("", (str1, str2) -> str1 + " " + str2);
        System.out.println(names);

        // 5. 返回所有交易員的姓名字母串, 并按字母順序排序
        String letters = transactions.stream()
                .map(transaction -> transaction.getTrader().getName())
                .distinct()
                .map(name -> name.split(""))
                .flatMap(Arrays::stream)
                .sorted()
                .collect(Collectors.joining());
        System.out.println(letters);

        // 6. 有沒(méi)有交易員是在米蘭工作
        boolean workMilan = transactions.stream()
                .anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan"));
        System.out.println(workMilan);

        // 7. 打印生活在劍橋的交易員的所有交易額總和
        long sum = transactions.stream()
                .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge"))
                .mapToLong(Transaction::getValue)
                .sum();
        System.out.println(sum);

        // 8. 所有交易中,最高的交易額是多少
        OptionalInt max = transactions.stream()
                .mapToInt(Transaction::getValue)
                .max();
        // or transactions.stream().map(Transaction::getValue).max(Comparator.naturalOrder());
        System.out.println(max.orElse(0));

        // 9. 找到交易額最小的交易
        Optional min = transactions.stream()
                .min(Comparator.comparingInt(Transaction::getValue));
        System.out.println(min.orElseThrow(IllegalArgumentException::new));
    }
}

在Java8中,可以在接口不變的情況下,將流改為并行流。這樣,就可以很自然地使用多線程進(jìn)行集合中的數(shù)據(jù)處理。

并行流與并行排序

demo:我們希望可以統(tǒng)計(jì)一個(gè)1~1000000內(nèi)所有的質(zhì)數(shù)的數(shù)量。

IntStream.range(1,1000000).parallel().filter(PrimeUtil::isPrime).count();

從集合得到并行流

List ss =new ArrayList();
...
double ave = ss.parallelStream().mapToInt(s->s.score).average().getAsDouble();

int[]arr = new int [10000000];
Arrarys.parallelSort(arr);
進(jìn)階:自己生成流

1、Stream.generate

通過(guò)實(shí)現(xiàn) Supplier 接口,你可以自己來(lái)控制流的生成。這種情形通常用于隨機(jī)數(shù)、常量的 Stream,或者需要前后元素間維持著某種狀態(tài)信息的 Stream。把 Supplier 實(shí)例傳遞給 Stream.generate() 生成的 Stream,默認(rèn)是串行(相對(duì) parallel 而言)但無(wú)序的(相對(duì) ordered 而言)。由于它是無(wú)限的,在管道中,必須利用 limit 之類的操作限制 Stream 大小。

//生成 10 個(gè)隨機(jī)整數(shù)
Random seed = new Random();
Supplier random = seed::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
//Another way
IntStream.generate(() -> (int) (System.nanoTime() % 100)).
limit(10).forEach(System.out::println);

注意幾個(gè)關(guān)鍵詞:默認(rèn)串行、無(wú)序、無(wú)限(需要進(jìn)行短路求值操作如limit)。

2、Stream.iterate

iterate 跟 reduce 操作很像,接受一個(gè)種子值,和一個(gè) UnaryOperator(例如 f)。然后種子值成為 Stream 的第一個(gè)元素,f(seed) 為第二個(gè),f(f(seed)) 第三個(gè),以此類推。

//生成一個(gè)等差數(shù)列 0 3 6 9 12 15 18 21 24 27
Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " "));.

與 Stream.generate 相仿,在 iterate 時(shí)候管道必須有 limit 這樣的操作來(lái)限制 Stream 大小。

進(jìn)階:用 Collectors 來(lái)進(jìn)行 reduction 操作

java.util.stream.Collectors 類的主要作用就是輔助進(jìn)行各類有用的 reduction 操作,例如轉(zhuǎn)變輸出為 Collection,把 Stream 元素進(jìn)行歸組。
1、groupingBy/partitioningBy

//按照年齡歸組
Map> personGroups = Stream.generate(new PersonSupplier()).
 limit(100).
 collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
 Map.Entry> persons = (Map.Entry) it.next();
 System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
}


//按照未成年人和成年人歸組
Map> children = Stream.generate(new PersonSupplier()).
 limit(100).
 collect(Collectors.partitioningBy(p -> p.getAge() < 18));
System.out.println("Children number: " + children.get(true).size());
System.out.println("Adult number: " + children.get(false).size());

//在使用條件“年齡小于 18”進(jìn)行分組后可以看到,不到 18 歲的未成年人是一組,成年人是另外一組。partitioningBy 其實(shí)是一種特殊的 groupingBy,它依照條件測(cè)試的是否兩種結(jié)果來(lái)構(gòu)造返回的數(shù)據(jù)結(jié)構(gòu),get(true) 和 get(false) 能即為全部的元素對(duì)象。
Stream總結(jié)

其實(shí)這里很多觀念和Spark的RDD操作很相似。
總之,Stream 的特性可以歸納為:

不是數(shù)據(jù)結(jié)構(gòu),它沒(méi)有內(nèi)部存儲(chǔ),它只是用操作管道從 source(數(shù)據(jù)結(jié)構(gòu)、數(shù)組、generator function、IO channel)抓取數(shù)據(jù)。

它也絕不修改自己所封裝的底層數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)。例如 Stream 的 filter 操作會(huì)產(chǎn)生一個(gè)不包含被過(guò)濾元素的新 Stream,而不是從 source 刪除那些元素。

所有 Stream 的操作必須以 lambda 表達(dá)式為參數(shù)

不支持索引訪問(wèn),你可以請(qǐng)求第一個(gè)元素,但無(wú)法請(qǐng)求第二個(gè),第三個(gè),或最后一個(gè)。

很容易生成數(shù)組或者 List

惰性化,Intermediate 操作永遠(yuǎn)是惰性化的。

很多 Stream 操作是向后延遲的,一直到它弄清楚了最后需要多少數(shù)據(jù)才會(huì)開(kāi)始。

并行能力,當(dāng)一個(gè) Stream 是并行化的,就不需要再寫多線程代碼,所有對(duì)它的操作會(huì)自動(dòng)并行進(jìn)行的。

可以是無(wú)限的,集合有固定大小,Stream 則不必。limit(n) 和 findFirst() 這類的 short-circuiting 操作可以對(duì)無(wú)限的 Stream 進(jìn)行運(yùn)算并很快完成。

注解的更新

對(duì)于注解,Java 8 主要有兩點(diǎn)改進(jìn):類型注解和重復(fù)注解。
Java 8 的類型注解擴(kuò)展了注解使用的范圍。在該版本之前,注解只能是在聲明的地方使用。現(xiàn)在幾乎可以為任何東西添加注解:局部變量、類與接口,就連方法的異常也能添加注解。新增的兩個(gè)注釋的程序元素類型 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER 用來(lái)描述注解的新場(chǎng)合。
ElementType.TYPE_PARAMETER 表示該注解能寫在類型變量的聲明語(yǔ)句中。而 ElementType.TYPE_USE 表示該注解能寫在使用類型的任何語(yǔ)句中(例如聲明語(yǔ)句、泛型和強(qiáng)制轉(zhuǎn)換語(yǔ)句中的類型)。
對(duì)類型注解的支持,增強(qiáng)了通過(guò)靜態(tài)分析工具發(fā)現(xiàn)錯(cuò)誤的能力。原先只能在運(yùn)行時(shí)發(fā)現(xiàn)的問(wèn)題可以提前在編譯的時(shí)候被排查出來(lái)。Java 8 本身雖然沒(méi)有自帶類型檢測(cè)的框架,但可以通過(guò)使用 Checker Framework 這樣的第三方工具,自動(dòng)檢查和確認(rèn)軟件的缺陷,提高生產(chǎn)效率。

在Java8之前使用注解的一個(gè)限制是相同的注解在同一位置只能聲明一次,不能聲明多次。Java 8 引入了重復(fù)注解機(jī)制,這樣相同的注解可以在同一地方聲明多次。重復(fù)注解機(jī)制本身必須用 @Repeatable 注解。

IO/NIO 的改進(jìn)

增加了一些新的 IO/NIO 方法,使用這些方法可以從文件或者輸入流中獲取流(java.util.stream.Stream),通過(guò)對(duì)流的操作,可以簡(jiǎn)化文本行處理、目錄遍歷和文件查找。
新增的 API 如下:

BufferedReader.line(): 返回文本行的流 Stream

File.lines(Path, Charset):返回文本行的流 Stream

File.list(Path): 遍歷當(dāng)前目錄下的文件和目錄

File.walk(Path, int, FileVisitOption): 遍歷某一個(gè)目錄下的所有文件和指定深度的子目錄

File.find(Path, int, BiPredicate, FileVisitOption... ): 查找相應(yīng)的文件

下面就是用流式操作列出當(dāng)前目錄下的所有文件和目錄:

Files.list(new File(".").toPath())
 .forEach(System.out::println);
新的Date/Time API

Java 的日期與時(shí)間 API 問(wèn)題由來(lái)已久,Java 8 之前的版本中關(guān)于時(shí)間、日期及其他時(shí)間日期格式化類由于線程安全、重量級(jí)、序列化成本高等問(wèn)題而飽受批評(píng)。Java 8 吸收了 Joda-Time 的精華,以一個(gè)新的開(kāi)始為 Java 創(chuàng)建優(yōu)秀的 API。新的 java.time 中包含了所有關(guān)于時(shí)鐘(Clock),本地日期(LocalDate)、本地時(shí)間(LocalTime)、本地日期時(shí)間(LocalDateTime)、時(shí)區(qū)(ZonedDateTime)和持續(xù)時(shí)間(Duration)的類。歷史悠久的 Date 類新增了 toInstant() 方法,用于把 Date 轉(zhuǎn)換成新的表示形式。這些新增的本地化時(shí)間日期 API 大大簡(jiǎn)化了了日期時(shí)間和本地化的管理。

Java日期/時(shí)間API包含以下相應(yīng)的包。

java.time包:這是新的Java日期/時(shí)間API的基礎(chǔ)包,所有的主要基礎(chǔ)類都是這個(gè)包的一部分,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration等等。所有這些類都是不可變的和線程安全的,在絕大多數(shù)情況下,這些類能夠有效地處理一些公共的需求。

java.time.chrono包:這個(gè)包為非ISO的日歷系統(tǒng)定義了一些泛化的API,我們可以擴(kuò)展AbstractChronology類來(lái)創(chuàng)建自己的日歷系統(tǒng)。

java.time.format包:這個(gè)包包含能夠格式化和解析日期時(shí)間對(duì)象的類,在絕大多數(shù)情況下,我們不應(yīng)該直接使用它們,因?yàn)?b>java.time包中相應(yīng)的類已經(jīng)提供了格式化和解析的方法。

java.time.temporal包:這個(gè)包包含一些時(shí)態(tài)對(duì)象,我們可以用其找出關(guān)于日期/時(shí)間對(duì)象的某個(gè)特定日期或時(shí)間,比如說(shuō),可以找到某月的第一天或最后一天。你可以非常容易地認(rèn)出這些方法,因?yàn)樗鼈兌季哂小皐ithXXX”的格式。

java.time.zone包:這個(gè)包包含支持不同時(shí)區(qū)以及相關(guān)規(guī)則的類。

例如,下面是對(duì) LocalDate,LocalTime 的簡(jiǎn)單應(yīng)用:

//LocalDate只保存日期系統(tǒng)的日期部分,有時(shí)區(qū)信息,LocalTime只保存時(shí)間部分,沒(méi)有時(shí)區(qū)信息。LocalDate和LocalTime都可以從Clock對(duì)象創(chuàng)建。
//LocalDate
LocalDate localDate = LocalDate.now(); //獲取本地日期
localDate = LocalDate.ofYearDay(2014, 200); // 獲得 2014 年的第 200 天 
System.out.println(localDate.toString());//輸出:2014-07-19
localDate = LocalDate.of(2014, Month.SEPTEMBER, 10); //2014 年 9 月 10 日 
System.out.println(localDate.toString());//輸出:2014-09-10
//LocalTime
LocalTime localTime = LocalTime.now(); //獲取當(dāng)前時(shí)間
System.out.println(localTime.toString());//輸出當(dāng)前時(shí)間
localTime = LocalTime.of(10, 20, 50);//獲得 10:20:50 的時(shí)間點(diǎn)
System.out.println(localTime.toString());//輸出: 10:20:50
//Clock 時(shí)鐘,Clock類可以替換 System.currentTimeMillis() 和 TimeZone.getDefault(). 如:Clock.systemDefaultZone().millis()
Clock clock = Clock.systemDefaultZone();//獲取系統(tǒng)默認(rèn)時(shí)區(qū) (當(dāng)前瞬時(shí)時(shí)間 )
long millis = clock.millis();//

//LocalDateTime類合并了LocalDate和LocalTime,它保存有ISO-8601日期系統(tǒng)的日期和時(shí)間,但是沒(méi)有時(shí)區(qū)信息。
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime );
System.out.println( datetimeFromClock );

//如果您需要一個(gè)類持有日期時(shí)間和時(shí)區(qū)信息,可以使用ZonedDateTime,它保存有ISO-8601日期系統(tǒng)的日期和時(shí)間,而且有時(shí)區(qū)信息。
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );

Duration類,Duration持有的時(shí)間精確到納秒。它讓我們很容易計(jì)算兩個(gè)日期中間的差異。讓我們來(lái)看一下:

// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
 
final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );

上面的例子計(jì)算了兩個(gè)日期(2014年4月16日和2014年5月16日)之間的持續(xù)時(shí)間(基于天數(shù)和小時(shí))

日期API操作

//日期算術(shù)操作,多數(shù)日期/時(shí)間API類都實(shí)現(xiàn)了一系列工具方法,如:加/減天數(shù)、周數(shù)、月份數(shù),等等。還有其他的工具方法能夠使用TemporalAdjuster調(diào)整日期,并計(jì)算兩個(gè)日期間的周期。
LocalDate today = LocalDate.now();
//Get the Year, check if it"s leap year
System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
//Compare two LocalDate for before and after
System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
//Create LocalDateTime from LocalDate
System.out.println("Current Time="+today.atTime(LocalTime.now()));
//plus and minus operations
System.out.println("10 days after today will be "+today.plusDays(10));
System.out.println("3 weeks after today will be "+today.plusWeeks(3));
System.out.println("20 months after today will be "+today.plusMonths(20));
System.out.println("10 days before today will be "+today.minusDays(10));
System.out.println("3 weeks before today will be "+today.minusWeeks(3));
System.out.println("20 months before today will be "+today.minusMonths(20));

//Temporal adjusters for adjusting the dates
System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
System.out.println("Last date of this year= "+lastDayOfYear);

Period period = today.until(lastDayOfYear);
System.out.println("Period Format= "+period);
System.out.println("Months remaining in the year= "+period.getMonths()); 

解析和格式化:將一個(gè)日期格式轉(zhuǎn)換為不同的格式,之后再解析一個(gè)字符串,得到日期時(shí)間對(duì)象

        //Format examples
        LocalDate date = LocalDate.now();
        //default format
        System.out.println("Default format of LocalDate="+date);
        //specific format
        System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
        System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
 
        LocalDateTime dateTime = LocalDateTime.now();
        //default format
        System.out.println("Default format of LocalDateTime="+dateTime);
        //specific format
        System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
        System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
 
        Instant timestamp = Instant.now();
        //default format
        System.out.println("Default format of Instant="+timestamp);
 
        //Parse examples
        LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48",
                DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
        System.out.println("Default format after parsing = "+dt);

舊的日期時(shí)間支持轉(zhuǎn)換:

        //Date to Instant
        Instant timestamp = new Date().toInstant();
        //Now we can convert Instant to LocalDateTime or other similar classes
        LocalDateTime date = LocalDateTime.ofInstant(timestamp, 
                        ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
        System.out.println("Date = "+date);
 
        //Calendar to Instant
        Instant time = Calendar.getInstance().toInstant();
        System.out.println(time);
        //TimeZone to ZoneId
        ZoneId defaultZone = TimeZone.getDefault().toZoneId();
        System.out.println(defaultZone);
 
        //ZonedDateTime from specific Calendar
        ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
        System.out.println(gregorianCalendarDateTime);
 
        //Date API to Legacy classes
        Date dt = Date.from(Instant.now());
        System.out.println(dt);
 
        TimeZone tz = TimeZone.getTimeZone(defaultZone);
        System.out.println(tz);
 
        GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
        System.out.println(gc);
Java8 CompletableFuture強(qiáng)大的函數(shù)式異步編程輔助類

Future是Java 5添加的類,用來(lái)描述一個(gè)異步計(jì)算的結(jié)果。你可以使用isDone方法檢查計(jì)算是否完成,或者使用get阻塞住調(diào)用線程,直到計(jì)算完成返回結(jié)果,你也可以使用cancel方法停止任務(wù)的執(zhí)行。
雖然Future以及相關(guān)使用方法提供了異步執(zhí)行任務(wù)的能力,但是對(duì)于結(jié)果的獲取卻是很不方便,只能通過(guò)阻塞或者輪詢的方式得到任務(wù)的結(jié)果。阻塞的方式顯然和我們的異步編程的初衷相違背,輪詢的方式又會(huì)耗費(fèi)無(wú)謂的CPU資源,而且也不能及時(shí)地得到計(jì)算結(jié)果,為什么不能用觀察者設(shè)計(jì)模式當(dāng)計(jì)算結(jié)果完成及時(shí)通知監(jiān)聽(tīng)者呢?
其實(shí)Google guava早就提供了通用的擴(kuò)展Future:ListenableFuture、SettableFuture 以及輔助類Futures等,方便異步編程。

final String name = ...;
inFlight.add(name);
ListenableFuture future = service.query(name);
future.addListener(new Runnable() {
  public void run() {
    processedCount.incrementAndGet();
    inFlight.remove(name);
    lastProcessed.set(name);
    logger.info("Done with {0}", name);
  }
}, executor);

在Java 8中, 新增加了一個(gè)包含50個(gè)方法左右的類: CompletableFuture,提供了非常強(qiáng)大的Future的擴(kuò)展功能,可以幫助我們簡(jiǎn)化異步編程的復(fù)雜性,提供了函數(shù)式編程的能力,可以通過(guò)回調(diào)的方式處理計(jì)算結(jié)果,并且提供了轉(zhuǎn)換和組合CompletableFuture的方法。
CompletableFuture類實(shí)現(xiàn)了CompletionStage和Future接口,所以你還是可以像以前一樣通過(guò)阻塞或者輪詢的方式獲得結(jié)果,盡管這種方式不推薦使用。
盡管Future可以代表在另外的線程中執(zhí)行的一段異步代碼,但是你還是可以在本身線程中執(zhí)行:

public class BasicMain {
    public static CompletableFuture compute() {
        final CompletableFuture future = new CompletableFuture<>();
        return future;
    }
    public static void main(String[] args) throws Exception {
        final CompletableFuture f = compute();
        class Client extends Thread {
            CompletableFuture f;
            Client(String threadName, CompletableFuture f) {
                super(threadName);
                this.f = f;
            }
            @Override
            public void run() {
                try {
                    System.out.println(this.getName() + ": " + f.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
        new Client("Client1", f).start();
        new Client("Client2", f).start();
        System.out.println("waiting");

        //上面的代碼中future沒(méi)有關(guān)聯(lián)任何的Callback、線程池、異步任務(wù)等,如果客戶端調(diào)用future.get就會(huì)一致傻等下去。除非我們顯式指定complete
        f.complete(100);
        //f.completeExceptionally(new Exception());
        System.in.read();
    }
}

CompletableFuture.complete()、CompletableFuture.completeExceptionally只能被調(diào)用一次。但是我們有兩個(gè)后門方法可以重設(shè)這個(gè)值:obtrudeValue、obtrudeException,但是使用的時(shí)候要小心,因?yàn)閏omplete已經(jīng)觸發(fā)了客戶端,有可能導(dǎo)致客戶端會(huì)得到不期望的結(jié)果。

1、創(chuàng)建CompletableFuture對(duì)象。

public static CompletableFuture completedFuture(U value)

public static CompletableFuture runAsync(Runnable runnable)

public static CompletableFuture runAsync(Runnable runnable, Executor executor)

public static CompletableFuture supplyAsync(Supplier supplier)

public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)
注意:以Async結(jié)尾并且沒(méi)有指定Executor的方法會(huì)使用ForkJoinPool.commonPool()作為它的線程池執(zhí)行異步代碼。因?yàn)榉椒ǖ膮?shù)類型都是函數(shù)式接口,所以可以使用lambda表達(dá)式實(shí)現(xiàn)異步任務(wù)

2、計(jì)算結(jié)果完成時(shí)的處理
當(dāng)CompletableFuture的計(jì)算結(jié)果完成,或者拋出異常的時(shí)候,我們可以執(zhí)行特定的Action。主要是下面的方法:

public CompletableFuture whenComplete(BiConsumer action)

public CompletableFuture whenCompleteAsync(BiConsumer action)

public CompletableFuture whenCompleteAsync(BiConsumer action, Executor executor)

public CompletableFuture exceptionally(Function fn)
注意:方法不以Async結(jié)尾,意味著Action使用相同的線程執(zhí)行,而Async可能會(huì)使用其它的線程去執(zhí)行(如果使用相同的線程池,也可能會(huì)被同一個(gè)線程選中執(zhí)行)

public class Main {
    private static Random rand = new Random();
    private static long t = System.currentTimeMillis();
    static int getMoreData() {
        System.out.println("begin to start compute");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("end to start compute. passed " + (System.currentTimeMillis() - t)/1000 + " seconds");
        return rand.nextInt(1000);
    }
    public static void main(String[] args) throws Exception {
        CompletableFuture future = CompletableFuture.supplyAsync(Main::getMoreData);
        Future f = future.whenComplete((v, e) -> {
            System.out.println(v);
            System.out.println(e);
        });
        System.out.println(f.get());
        System.in.read();
    }
}

下面一組方法雖然也返回CompletableFuture對(duì)象,但是對(duì)象的值和原來(lái)的CompletableFuture計(jì)算的值不同。當(dāng)原先的CompletableFuture的值計(jì)算完成或者拋出異常的時(shí)候,會(huì)觸發(fā)這個(gè)CompletableFuture對(duì)象的計(jì)算,結(jié)果由BiFunction參數(shù)計(jì)算而得。
因此這組方法兼有whenComplete和轉(zhuǎn)換的兩個(gè)功能。

public CompletableFuture handle(BiFunction fn)

public CompletableFuture handleAsync(BiFunction fn)

public CompletableFuture handleAsync(BiFunction fn, Executor executor)
同樣,不以Async結(jié)尾的方法由原來(lái)的線程計(jì)算,以Async結(jié)尾的方法由默認(rèn)的線程池ForkJoinPool.commonPool()或者指定的線程池executor運(yùn)行。

3、轉(zhuǎn)換
CompletableFuture可以作為monad(單子)和functor。由于回調(diào)風(fēng)格的實(shí)現(xiàn),我們不必因?yàn)榈却粋€(gè)計(jì)算完成而阻塞著調(diào)用線程,而是告訴CompletableFuture當(dāng)計(jì)算完成的時(shí)候請(qǐng)執(zhí)行某個(gè)function。而且我們還可以將這些操作串聯(lián)起來(lái),或者將CompletableFuture組合起來(lái)。

public CompletableFuture thenApply(Function fn)

public CompletableFuture thenApplyAsync(Function fn)

public CompletableFuture thenApplyAsync(Function fn, Executor executor)
這一組函數(shù)的功能是當(dāng)原來(lái)的CompletableFuture計(jì)算完后,將結(jié)果傳遞給函數(shù)fn,將fn的結(jié)果作為新的CompletableFuture計(jì)算結(jié)果。因此它的功能相當(dāng)于將CompletableFuture轉(zhuǎn)換成CompletableFuture

它們與handle方法的區(qū)別在于handle方法會(huì)處理正常計(jì)算值和異常,因此它可以屏蔽異常,避免異常繼續(xù)拋出。而thenApply方法只是用來(lái)處理正常值,因此一旦有異常就會(huì)拋出。

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    return 100;
});
CompletableFuture f =  future.thenApplyAsync(i -> i * 10).thenApply(i -> i.toString());
System.out.println(f.get()); //"1000"

需要注意的是,這些轉(zhuǎn)換并不是馬上執(zhí)行的,也不會(huì)阻塞,而是在前一個(gè)stage完成后繼續(xù)執(zhí)行。

4、純消費(fèi)(執(zhí)行Action)
上面的方法是當(dāng)計(jì)算完成的時(shí)候,會(huì)生成新的計(jì)算結(jié)果(thenApply, handle),或者返回同樣的計(jì)算結(jié)果whenComplete,CompletableFuture還提供了一種處理結(jié)果的方法,只對(duì)結(jié)果執(zhí)行Action,而不返回新的計(jì)算值,因此計(jì)算值為Void:

public CompletableFuture thenAccept(Consumer action)

public CompletableFuture thenAcceptAsync(Consumer action)

public CompletableFuture thenAcceptAsync(Consumer action, Executor executor)
看它的參數(shù)類型也就明白了,它們是函數(shù)式接口Consumer,這個(gè)接口只有輸入,沒(méi)有返回值。

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    return 100;
});
CompletableFuture f =  future.thenAccept(System.out::println);
System.out.println(f.get());

thenAcceptBoth以及相關(guān)方法提供了類似的功能,當(dāng)兩個(gè)CompletionStage都正常完成計(jì)算的時(shí)候,就會(huì)執(zhí)行提供的action,它用來(lái)組合另外一個(gè)異步的結(jié)果。
runAfterBoth是當(dāng)兩個(gè)CompletionStage都正常完成計(jì)算的時(shí)候,執(zhí)行一個(gè)Runnable,這個(gè)Runnable并不使用計(jì)算的結(jié)果。

public CompletableFuture thenAcceptBoth(CompletionStage other, BiConsumer action)

public CompletableFuture thenAcceptBothAsync(CompletionStage other, BiConsumer action)

public CompletableFuture thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor)

public CompletableFuture runAfterBoth(CompletionStage other, Runnable action)

例子如下:

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    return 100;
});
CompletableFuture f =  future.thenAcceptBoth(CompletableFuture.completedFuture(10), (x, y) -> System.out.println(x * y));
System.out.println(f.get());

更徹底地,下面一組方法當(dāng)計(jì)算完成的時(shí)候會(huì)執(zhí)行一個(gè)Runnable,與thenAccept不同,Runnable并不使用CompletableFuture計(jì)算的結(jié)果。

public CompletableFuture thenRun(Runnable action)

public CompletableFuture thenRunAsync(Runnable action)

public CompletableFuture thenRunAsync(Runnable action, Executor executor)

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    return 100;
});
CompletableFuture f =  future.thenRun(() -> System.out.println("finished"));
System.out.println(f.get());

因此,你可以根據(jù)方法的參數(shù)的類型來(lái)加速你的記憶。Runnable類型的參數(shù)會(huì)忽略計(jì)算的結(jié)果,Consumer是純消費(fèi)計(jì)算結(jié)果,BiConsumer會(huì)組合另外一個(gè)CompletionStage純消費(fèi),F(xiàn)unction會(huì)對(duì)計(jì)算結(jié)果做轉(zhuǎn)換,BiFunction會(huì)組合另外一個(gè)CompletionStage的計(jì)算結(jié)果做轉(zhuǎn)換。

5、組合compose

public CompletableFuture thenCompose(Function> fn)

public CompletableFuture thenComposeAsync(Function> fn)

public CompletableFuture thenComposeAsync(Function> fn, Executor executor)
這一組方法接受一個(gè)Function作為參數(shù),這個(gè)Function的輸入是當(dāng)前的CompletableFuture的計(jì)算值,返回結(jié)果將是一個(gè)新的CmpletableFuture,這個(gè)新的CompletableFuture會(huì)組合原來(lái)的CompletableFuture和函數(shù)返回的CompletableFuture。

public CompletableFuture thenCombine(CompletionStage other, BiFunction fn)

public CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn)

public CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor)
兩個(gè)CompletionStage是并行執(zhí)行的,它們之間并沒(méi)有先后依賴順序,other并不會(huì)等待先前的CompletableFuture執(zhí)行完畢后再執(zhí)行。

6、Either
thenAcceptBoth和runAfterBoth是當(dāng)兩個(gè)CompletableFuture都計(jì)算完成,而我們下面要了解的方法是當(dāng)任意一個(gè)CompletableFuture計(jì)算完成的時(shí)候就會(huì)執(zhí)行。

public CompletableFuture acceptEither(CompletionStage other, Consumer action)

public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action)

public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action, Executor executor)

public CompletableFuture applyToEither(CompletionStage other, Function fn)

public CompletableFuture applyToEitherAsync(CompletionStage other, Function fn)

public CompletableFuture applyToEitherAsync(CompletionStage other, Function fn, Executor executor)
下面這個(gè)例子有時(shí)會(huì)輸出100,有時(shí)候會(huì)輸出200,哪個(gè)Future先完成就會(huì)根據(jù)它的結(jié)果計(jì)算。

Random rand = new Random();
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(10000 + rand.nextInt(1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 100;
});
CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(10000 + rand.nextInt(1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 200;
});
CompletableFuture f =  future.applyToEither(future2,i -> i.toString());

7、輔助方法 allOf 和 anyOf
用來(lái)組合多個(gè)CompletableFuture。

public static CompletableFuture allOf(CompletableFuture... cfs)

public static CompletableFuture anyOf(CompletableFuture... cfs)
allOf方法是當(dāng)所有的CompletableFuture都執(zhí)行完后執(zhí)行計(jì)算。

anyOf方法是當(dāng)任意一個(gè)CompletableFuture執(zhí)行完后就會(huì)執(zhí)行計(jì)算,計(jì)算的結(jié)果相同。

8、更進(jìn)一步
如果你用過(guò)Guava的Future類,你就會(huì)知道它的Futures輔助類提供了很多便利方法,用來(lái)處理多個(gè)Future,而不像Java的CompletableFuture,只提供了allOf、anyOf兩個(gè)方法。
比如有這樣一個(gè)需求,將多個(gè)CompletableFuture組合成一個(gè)CompletableFuture,這個(gè)組合后的CompletableFuture的計(jì)算結(jié)果是個(gè)List,它包含前面所有的CompletableFuture的計(jì)算結(jié)果,guava的Futures.allAsList可以實(shí)現(xiàn)這樣的功能.
但是對(duì)于java CompletableFuture,我們需要一些輔助方法:

   public static  CompletableFuture> sequence(List> futures) {
       CompletableFuture allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
       return allDoneFuture.thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
   }
public static  CompletableFuture> sequence(Stream> futures) {
       List> futureList = futures.filter(f -> f != null).collect(Collectors.toList());
       return sequence(futureList);
   }

Java Future轉(zhuǎn)CompletableFuture:

public static  CompletableFuture toCompletable(Future future, Executor executor) {
    return CompletableFuture.supplyAsync(() -> {
        try {
            return future.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
    }, executor);
}

github有多個(gè)項(xiàng)目可以實(shí)現(xiàn)Java CompletableFuture與其它Future (如Guava ListenableFuture)之間的轉(zhuǎn)換,如spotify/futures-extra、future-converter、scala/scala-java8-compat 等。

其他 Java 8 Optional類

大家可能都有這樣的經(jīng)歷:調(diào)用一個(gè)方法得到了返回值卻不能直接將返回值作為參數(shù)去調(diào)用別的方法。我們首先要判斷這個(gè)返回值是否為null,只有在非空的前提下才能將其作為其他方法的參數(shù)。
Java 8引入了一個(gè)新的Optional類。Optional類的Javadoc描述如下:這是一個(gè)可以為null的容器對(duì)象。如果值存在則isPresent()方法會(huì)返回true,調(diào)用get()方法會(huì)返回該對(duì)象。
1、of:為非null的值創(chuàng)建一個(gè)Optional。需要注意的是,創(chuàng)建對(duì)象時(shí)傳入的參數(shù)不能為null。如果傳入?yún)?shù)為null,則拋出NullPointerException

//調(diào)用工廠方法創(chuàng)建Optional實(shí)例
Optional name = Optional.of("Sanaulla");
//錯(cuò)誤寫法,傳入?yún)?shù)為null,拋出NullPointerException.
Optional someNull = Optional.of(null);

2、ofNullable:為指定的值創(chuàng)建一個(gè)Optional,如果指定的值為null,則返回一個(gè)空的Optional。
ofNullable與of方法相似,唯一的區(qū)別是可以接受參數(shù)為null的情況
3、isPresent:如果值存在返回true,否則返回false。
4、get:如果Optional有值則將其返回,否則拋出NoSuchElementException。
5、ifPresent:如果Optional實(shí)例有值則為其調(diào)用consumer,否則不做處理
要理解ifPresent方法,首先需要了解Consumer類。簡(jiǎn)答地說(shuō),Consumer類包含一個(gè)抽象方法。該抽象方法對(duì)傳入的值進(jìn)行處理,但沒(méi)有返回值。Java8支持不用接口直接通過(guò)lambda表達(dá)式傳入?yún)?shù)。

//ifPresent方法接受lambda表達(dá)式作為參數(shù)。
//lambda表達(dá)式對(duì)Optional的值調(diào)用consumer進(jìn)行處理。
name.ifPresent((value) -> {
  System.out.println("The length of the value is: " + value.length());
});

6、orElse:如果有值則將其返回,否則返回orElse方法傳入的參數(shù)。
7、orElseGet:orElseGet與orElse方法類似,區(qū)別在于得到的默認(rèn)值。orElse方法將傳入的字符串作為默認(rèn)值,orElseGet方法可以接受Supplier接口的實(shí)現(xiàn)用來(lái)生成默認(rèn)值。示例如下:

//orElseGet與orElse方法類似,區(qū)別在于orElse傳入的是默認(rèn)值,
//orElseGet可以接受一個(gè)lambda表達(dá)式生成默認(rèn)值。
//輸出:Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//輸出:Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));

8、orElseThrow:如果有值則將其返回,否則拋出supplier接口創(chuàng)建的異常。
在orElseGet方法中,我們傳入一個(gè)Supplier接口。然而,在orElseThrow中我們可以傳入一個(gè)lambda表達(dá)式或方法,如果值不存在來(lái)拋出異常。示例如下:

try {
  //orElseThrow與orElse方法類似。與返回默認(rèn)值不同,
  //orElseThrow會(huì)拋出lambda表達(dá)式或方法生成的異常 
 
  empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
  //輸出: No value present in the Optional instance
  System.out.println(ex.getMessage());
}

9、map:如果有值,則對(duì)其執(zhí)行調(diào)用mapping函數(shù)得到返回值。如果返回值不為null,則創(chuàng)建包含mapping返回值的Optional作為map方法返回值,否則返回空Optional。
map方法用來(lái)對(duì)Optional實(shí)例的值執(zhí)行一系列操作。

//map方法執(zhí)行傳入的lambda表達(dá)式參數(shù)對(duì)Optional實(shí)例的值進(jìn)行修改。
//為lambda表達(dá)式的返回值創(chuàng)建新的Optional實(shí)例作為map方法的返回值。
Optional upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));

10、flatMap:如果有值,為其執(zhí)行mapping函數(shù)返回Optional類型返回值,否則返回空Optional。flatMap與map(Funtion)方法類似,區(qū)別在于flatMap中的mapper返回值必須是Optional。調(diào)用結(jié)束時(shí),flatMap不會(huì)對(duì)結(jié)果用Optional封裝。

//flatMap與map(Function)非常類似,區(qū)別在于傳入方法的lambda表達(dá)式的返回類型。
//map方法中的lambda表達(dá)式返回值可以是任意類型,在map函數(shù)返回之前會(huì)包裝為Optional。 
//但flatMap方法中的lambda表達(dá)式返回值必須是Optionl實(shí)例。 
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//輸出SANAULLA

11、filter:如果有值并且滿足斷言條件返回包含該值的Optional,否則返回空Optional。

//filter方法檢查給定的Option值是否滿足某些條件。
//如果滿足則返回同一個(gè)Option實(shí)例,否則返回空Optional。
Optional longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//輸出Sanaulla
 
//另一個(gè)例子是Optional值不滿足filter指定的條件。
Optional anotherName = Optional.of("Sana");
Optional shortName = anotherName.filter((value) -> value.length() > 6);
//輸出:name長(zhǎng)度不足6字符
System.out.println(shortName.orElse("The name is less than 6 characters"));
Java8編譯器的新特性

1、參數(shù)名字
很長(zhǎng)時(shí)間以來(lái),Java程序員想盡辦法把參數(shù)名字保存在java字節(jié)碼里,并且讓這些參數(shù)名字在運(yùn)行時(shí)可用。Java 8 終于把這個(gè)需求加入到了Java語(yǔ)言(使用反射API和Parameter.getName() 方法)和字節(jié)碼里(使用java編譯命令javac的–parameters參數(shù))。

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
 
public class ParameterNames {
public static void main(String[] args) throws Exception {
Method method = ParameterNames.class.getMethod( "main", String[].class );
for( final Parameter parameter: method.getParameters() ) {
System.out.println( "Parameter: " + parameter.getName() );
}
}
}

額外的,有一個(gè)方便的方法Parameter.isNamePresent() 來(lái)驗(yàn)證參數(shù)名是不是可用。

如果你編譯這個(gè)class的時(shí)候沒(méi)有添加參數(shù)–parameters,運(yùn)行的時(shí)候你會(huì)得到這個(gè)結(jié)果:
Parameter: arg0
編譯的時(shí)候添加了–parameters參數(shù)的話,運(yùn)行結(jié)果會(huì)不一樣:
Parameter: args
對(duì)于有經(jīng)驗(yàn)的Maven使用者,–parameters參數(shù)可以添加到maven-compiler-plugin的配置部分:


    org.apache.maven.plugins
    maven-compiler-plugin
    3.1
    
        -parameters
        1.8
        1.8
    

eclipse中也可以進(jìn)行相關(guān)配置:

Nashorn javascript引擎

Java 8提供了一個(gè)新的Nashorn javascript引擎,它允許我們?cè)贘VM上運(yùn)行特定的javascript應(yīng)用。Nashorn javascript引擎只是javax.script.ScriptEngine另一個(gè)實(shí)現(xiàn),而且規(guī)則也一樣,允許Java和JavaScript互相操作。這里有個(gè)小例子:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( "JavaScript" );
System.out.println( engine.getClass().getName() );
System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;");

推薦參考:http://www.importnew.com/2266...

Base64

對(duì)Base64的支持最終成了Java 8標(biāo)準(zhǔn)庫(kù)的一部分,非常簡(jiǎn)單易用:

import java.nio.charset.StandardCharsets;
import java.util.Base64;
 
public class Base64s {
public static void main(String[] args) {
final String text = "Base64 finally in Java 8!";
 
final String encoded = Base64
.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println( encoded );
 
final String decoded = new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( decoded );
}
}

新的Base64API也支持URL和MINE的編

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67340.html

相關(guān)文章

  • Java8實(shí)戰(zhàn)》-讀書筆記第一章(02)

    摘要:實(shí)戰(zhàn)讀書筆記第一章從方法傳遞到接著上次的,繼續(xù)來(lái)了解一下,如果繼續(xù)簡(jiǎn)化代碼。去掉并且生成的數(shù)字是萬(wàn),所消耗的時(shí)間循序流并行流至于為什么有時(shí)候并行流效率比循序流還低,這個(gè)以后的文章會(huì)解釋。 《Java8實(shí)戰(zhàn)》-讀書筆記第一章(02) 從方法傳遞到Lambda 接著上次的Predicate,繼續(xù)來(lái)了解一下,如果繼續(xù)簡(jiǎn)化代碼。 把方法作為值來(lái)傳遞雖然很有用,但是要是有很多類似與isHeavy...

    lushan 評(píng)論0 收藏0
  • Jdk1.8新特學(xué)習(xí)(Optional)

    摘要:它的出現(xiàn)是為我們解決空指針異常的,以前我們寫代碼如果不進(jìn)行判斷,會(huì)經(jīng)常出現(xiàn)異常。因?yàn)樗旧砭褪莻€(gè)對(duì)象,不管放進(jìn)去的對(duì)象為不為,始終不會(huì)返回,所以你也不需要在你的業(yè)務(wù)流程中進(jìn)行一大堆的判斷,避免了程序運(yùn)行時(shí)的空指針異常。 想必大家已經(jīng)在使用jdk1.8做項(xiàng)目開(kāi)發(fā),但是你對(duì)于它里面的一些性特性了解多少呢?有沒(méi)有在你的項(xiàng)目中運(yùn)用呢?現(xiàn)在就和我來(lái)一起梳理一下吧。 介紹 它是java.util包...

    liaosilzu2007 評(píng)論0 收藏0
  • CSS3新特學(xué)習(xí)

    摘要:引言最近發(fā)現(xiàn)很多的新特性不熟悉,所以今天把它們都學(xué)習(xí)一邊,做出效果加深印象,說(shuō)到還加了蠻多的特性,像一些的一些效果,動(dòng)畫屬性等。 引言 最近發(fā)現(xiàn)很多css3的新特性不熟悉,所以今天把它們都學(xué)習(xí)一邊,做出效果加深印象,說(shuō)到css3還加了蠻多的特性,像一些border的一些效果,動(dòng)畫屬性animation trasiform等。 1.border-radius(邊框圓角) 效果 showI...

    wing324 評(píng)論0 收藏0
  • Es6新特學(xué)習(xí)

    摘要:基礎(chǔ)語(yǔ)法變量提升都可以個(gè)難點(diǎn)在編譯時(shí)執(zhí)行并沒(méi)有報(bào)錯(cuò),執(zhí)行結(jié)果如圖注意結(jié)果沒(méi)有變更改結(jié)果值變了參考新特性未完一直更新 基礎(chǔ)語(yǔ)法 變量提升 //es5 var arr = []; for(var i=0; i

    AlphaWallet 評(píng)論0 收藏0
  • JDK 1.8 新特學(xué)習(xí)(Stream)

    摘要:會(huì)在數(shù)據(jù)源內(nèi)部隱式的遍歷進(jìn)行處理。會(huì)并行遍歷數(shù)據(jù),將數(shù)據(jù)分成若干段,同時(shí)進(jìn)行處理,最終匯總結(jié)果一起輸出。結(jié)束操作會(huì)觸發(fā)實(shí)際計(jì)算,計(jì)算發(fā)生時(shí)會(huì)把所有中間操作積攢的操作以的方式執(zhí)行,這樣可以減少迭代次數(shù)。為函數(shù)式編程而生。 Stream實(shí)現(xiàn)了對(duì)數(shù)據(jù)源的流式處理,它可以并行操作,提高數(shù)據(jù)處理效率。 什么是流 流不是集合,它不對(duì)數(shù)據(jù)做保存,只是最數(shù)據(jù)進(jìn)行算法處理,比如最大值,最小值,排序等操作...

    yhaolpz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<