從JAVA5到JAVA10新特性總結

本文參考或者摘抄:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

目錄

JDK5新特性

1:自動裝箱與拆箱:

2:枚舉

3:靜態導入

4:可變參數(Varargs)

5:內省(Introspector)

6:泛型(Generic)

7:For-Each循環

JDK 6新特性

1:Desktop類和SystemTray類

2:使用JAXB2來實現對象與XML之間的映射

3:理解StAX

4.使用Compiler API

5:輕量級Http Server API

6:插入式註解處理API(Pluggable Annotation Processing API)

7:用Console開發控制檯程序

8:對腳本語言的支持如: ruby, groovy, javascript

9:Common Annotations

JDK 7 新特性

1:switch中可以使用字符串

2:”<>”的運用List tempList = new ArrayList<>(); 即泛型實例化類型自動推斷。

3:自定義自動關閉類 

4:在try catch異常撲捉中,一個catch可以寫多個異常類型,用”|”隔開**

JDK 8新特性

Lambda表達式

1.什麼是Lambda?

函數式接口

默認方法

Stream

流的構成

flatMap

filter

limit-skip

sorted

min/max/distinct

Match

Collectors

統計

toArray

結束語


JDK5新特性

1:自動裝箱與拆箱:

自動裝箱的過程:每當需要一種類型的對象時,這種基本類型就自動地封裝到與它相同類型的包裝類中。

 

自動拆箱的過程:每當需要一個值時,被包裝對象中的值就被自動地提取出來,沒必要再去調用intValue()和doubleValue()方法。

 

自動裝箱,只需將該值賦給一個類型包裝器引用,java會自動創建一個對象。

 

自動拆箱,只需將該對象值賦給一個基本類型即可。

 

類型包裝器有:Double,Float,Long,Integer,Short,Character和Boolean

 

2:枚舉

 

把集合裏的對象元素一個一個提取出來。枚舉類型使代碼更具可讀性,理解清晰,易於維護。枚舉類型是強類型的,從而保證了系統安全性。而以類的靜態字段實現的類似替代模型,不具有枚舉的簡單性和類型安全性。

 

簡單的用法:JavaEnum簡單的用法一般用於代表一組常用常量,可用來代表一類相同類型的常量值。

 

複雜用法:Java爲枚舉類型提供了一些內置的方法,同事枚舉常量還可以有自己的方法。可以很方便的遍歷枚舉對象。

 

3:靜態導入

 

通過使用 import static,就可以不用指定 Constants 類名而直接使用靜態成員,包括靜態方法。

 

import xxxx 和 import static xxxx的區別是前者一般導入的是類文件如import java.util.Scanner;後者一般是導入靜態的方法,import static java.lang.System.out。

 

4:可變參數(Varargs)

 

可變參數的簡單語法格式爲:

 

methodName([argumentList], dataType… argumentName);

 

5:內省(Introspector)

 

內省是Java語言對Bean類屬性、事件的一種缺省處理方法。例如類A中有屬性name,那我們可以通過getName,setName來得到其值或者設置新的值。通過getName/setName來訪問name屬性,這就是默認的規則。Java中提供了一套API用來訪問某個屬性的getter /setter方法,通過這些API可以使你不需要了解這個規則(但你最好還是要搞清楚),這些API存放於包java.beans中。

 

一 般的做法是通過類Introspector來獲取某個對象的BeanInfo信息,然後通過BeanInfo來獲取屬性的描述器 (PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應的getter/setter方法,然後我們就可以通過反射機制來調用這些方法。

 

6:泛型(Generic)

 

C++ 通過模板技術可以指定集合的元素類型,而Java在1.5之前一直沒有相對應的功能。一個集合可以放任何類型的對象,相應地從集合裏面拿對象的時候我們也不得不對他們進行強制得類型轉換。引入了泛型,它允許指定集合裏元素的類型,這樣你可以得到強類型在編譯時刻進行類型檢查的好處。

 

7:For-Each循環

 

For-Each循環得加入簡化了集合的遍歷。假設我們要遍歷一個集合對其中的元素進行一些處理。

 

 

JDK 6新特性

1:Desktop類和SystemTray類

 

在JDK6中 ,AWT新增加了兩個類:Desktop和SystemTray。

 

前者可以用來打開系統默認瀏覽器瀏覽指定的URL,打開系統默認郵件客戶端給指定的郵箱發郵件,用默認應用程序打開或編輯文件(比如,用記事本打開以txt爲後綴名的文件),用系統默認的打印機打印文檔;後者可以用來在系統托盤區創建一個托盤程序.

 

2:使用JAXB2來實現對象與XML之間的映射

 

JAXB是Java Architecture for XML Binding的縮寫,可以將一個Java對象轉變成爲XML格式,反之亦然。

 

我們把對象與關係數據庫之間的映射稱爲ORM, 其實也可以把對象與XML之間的映射稱爲OXM(Object XML Mapping). 原來JAXB是Java EE的一部分,在JDK6中,SUN將其放到了Java SE中,這也是SUN的一貫做法。JDK6中自帶的這個JAXB版本是2.0, 比起1.0(JSR 31)來,JAXB2(JSR 222)用JDK5的新特性Annotation來標識要作綁定的類和屬性等,這就極大簡化了開發的工作量。

 

實 際上,在Java EE 5.0中,EJB和Web Services也通過Annotation來簡化開發工作。另外,JAXB2在底層是用StAX(JSR 173)來處理XML文檔。除了JAXB之外,我們還可以通過XMLBeans和Castor等來實現同樣的功能。

 

3:理解StAX

 

StAX(JSR 173)是JDK6.0中除了DOM和SAX之外的又一種處理XML文檔的API。

 

StAX 的來歷 :在JAXP1.3(JSR 206)有兩種處理XML文檔的方法:DOM(Document Object Model)和SAX(Simple API for XML).

 

由 於JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都會用到StAX,所以Sun決定把StAX加入到JAXP家族當中來,並將JAXP的版本升級到1.4(JAXP1.4是JAXP1.3的維護版本). JDK6裏面JAXP的版本就是1.4. 。

 

StAX是The Streaming API for XML的縮寫,一種利用拉模式解析(pull-parsing)XML文檔的API.StAX通過提供一種基於事件迭代器(Iterator)的API讓程序員去控制xml文檔解析過程,程序遍歷這個事件迭代器去處理每一個解析事件,解析事件可以看做是程序拉出來的,也就是程序促使解析器產生一個解析事件,然後處理該事件,之後又促使解析器產生下一個解析事件,如此循環直到碰到文檔結束符;

 

SAX也是基於事件處理xml文檔,但卻是用推模式解析,解析器解析完整個xml文檔後,才產生解析事件,然後推給程序去處理這些事件;DOM 採用的方式是將整個xml文檔映射到一顆內存樹,這樣就可以很容易地得到父節點和子結點以及兄弟節點的數據,但如果文檔很大,將會嚴重影響性能。

 

4.使用Compiler API

 

現在我們可以用JDK6 的Compiler API(JSR 199)去動態編譯Java源文件,Compiler API結合反射功能就可以實現動態的產生Java代碼並編譯執行這些代碼,有點動態語言的特徵。

 

這個特性對於某些需要用到動態編譯的應用程序相當有用,比如JSP Web Server,當我們手動修改JSP後,是不希望需要重啓Web Server纔可以看到效果的,這時候我們就可以用Compiler API來實現動態編譯JSP文件,當然,現在的JSP Web Server也是支持JSP熱部署的,現在的JSP Web Server通過在運行期間通過Runtime.exec或ProcessBuilder來調用javac來編譯代碼,這種方式需要我們產生另一個進程去做編譯工作,不夠優雅而且容易使代碼依賴與特定的操作系統;Compiler API通過一套易用的標準的API提供了更加豐富的方式去做動態編譯,而且是跨平臺的。

 

5:輕量級Http Server API

 

JDK6 提供了一個簡單的Http Server API,據此我們可以構建自己的嵌入式Http Server,它支持Http和Https協議,提供了HTTP1.1的部分實現,沒有被實現的那部分可以通過擴展已有的Http Server API來實現,程序員必須自己實現HttpHandler接口,HttpServer會調用HttpHandler實現類的回調方法來處理客戶端請求,在 這裏,我們把一個Http請求和它的響應稱爲一個交換,包裝成HttpExchange類,HttpServer負責將HttpExchange傳給 HttpHandler實現類的回調方法.

 

6:插入式註解處理API(Pluggable Annotation Processing API)

 

插入式註解處理API(JSR 269)提供一套標準API來處理Annotations(JSR 175)

 

實 際上JSR 269不僅僅用來處理Annotation,我覺得更強大的功能是它建立了Java 語言本身的一個模型,它把method, package, constructor, type, variable, enum, annotation等Java語言元素映射爲Types和Elements(兩者有什麼區別?), 從而將Java語言的語義映射成爲對象, 我們可以在javax.lang.model包下面可以看到這些類. 所以我們可以利用JSR 269提供的API來構建一個功能豐富的元編程(metaprogramming)環境.

 

JSR 269用Annotation Processor在編譯期間而不是運行期間處理Annotation, Annotation Processor相當於編譯器的一個插件,所以稱爲插入式註解處理.如果Annotation Processor處理Annotation時(執行process方法)產生了新的Java代碼,編譯器會再調用一次Annotation Processor,如果第二次處理還有新代碼產生,就會接着調用Annotation Processor,直到沒有新代碼產生爲止.每執行一次process()方法被稱爲一個”round”,這樣整個Annotation processing過程可以看作是一個round的序列.

 

JSR 269主要被設計成爲針對Tools或者容器的API. 舉個例子,我們想建立一套基於Annotation的單元測試框架(如TestNG),在測試類裏面用Annotation來標識測試期間需要執行的測試方法

 

7:用Console開發控制檯程序

 

JDK6 中提供了java.io.Console 類專用來訪問基於字符的控制檯設備. 你的程序如果要與Windows下的cmd或者Linux下的Terminal交互,就可以用Console類代勞. 但我們不總是能得到可用的Console, 一個JVM是否有可用的Console依賴於底層平臺和JVM如何被調用. 如果JVM是在交互式命令行(比如Windows的cmd)中啓動的,並且輸入輸出沒有重定向到另外的地方,那麼就可以得到一個可用的Console實例.

 

8:對腳本語言的支持如: ruby, groovy, javascript

 

9:Common Annotations

 

Common annotations原本是Java EE 5.0(JSR 244)規範的一部分,現在SUN把它的一部分放到了Java SE 6.0中.

 

隨 着Annotation元數據功能(JSR 175)加入到Java SE 5.0裏面,很多Java 技術(比如EJB,Web Services)都會用Annotation部分代替XML文件來配置運行參數(或者說是支持聲明式編程,如EJB的聲明式事務), 如果這些技術爲通用目的都單獨定義了自己的Annotations,顯然有點重複建設, 所以,爲其他相關的Java技術定義一套公共的Annotation是有價值的,可以避免重複建設的同時,也保證Java SE和Java EE 各種技術的一致性.

 

下面列舉出Common Annotations 1.0裏面的10個Annotations Common Annotations

 

Annotation Retention Target Description

 

Generated Source ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE 用於標註生成的源代碼

 

Resource Runtime TYPE, METHOD, FIELD 用於標註所依賴的資源,容器據此注入外部資源依賴,有基於字段的注入和基於setter方法的注入兩種方式

 

Resources Runtime TYPE 同時標註多個外部依賴,容器會把所有這些外部依賴注入

 

PostConstruct Runtime METHOD 標註當容器注入所有依賴之後運行的方法,用來進行依賴注入後的初始化工作,只有一個方法可以標註爲PostConstruct

 

PreDestroy Runtime METHOD 當對象實例將要被從容器當中刪掉之前,要執行的回調方法要標註爲PreDestroy RunAs Runtime TYPE 用於標註用什麼安全角色來執行被標註類的方法,這個安全角色必須和Container 的Security角色一致的。RolesAllowed Runtime TYPE, METHOD 用於標註允許執行被標註類或方法的安全角色,這個安全角色必須和Container 的Security角色一致的

 

PermitAll Runtime TYPE, METHOD 允許所有角色執行被標註的類或方法

 

DenyAll Runtime TYPE, METHOD 不允許任何角色執行被標註的類或方法,表明該類或方法不能在Java EE容器裏面運行

 

DeclareRoles Runtime TYPE 用來定義可以被應用程序檢驗的安全角色,通常用isUserInRole來檢驗安全角色

注意:

 

1.RolesAllowed,PermitAll,DenyAll不能同時應用到一個類或方法上

 

2.標註在方法上的RolesAllowed,PermitAll,DenyAll會覆蓋標註在類上的RolesAllowed,PermitAll,DenyAll

 

3.RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles還沒有加到Java SE 6.0上來

 

4.處理以上Annotations的工作是由Java EE容器來做, Java SE 6.0只是包含了上面表格的前五種Annotations的定義類,並沒有包含處理這些Annotations的引擎,這個工作可以由Pluggable Annotation Processing API(JSR 269)來做

 

改動的地方最大的就是java GUI界面的顯示了,JDK6.0(也就是JDK1.6)支持最新的windows vista系統的Windows Aero視窗效果,而JDK1.5不支持!!!

 

你要在vista環境下編程的話最好裝jdk6.0,否則它總是換到windows basic視窗效果.

 

 

JDK 7 新特性

1:switch中可以使用字符串

public static void testSwitch() {

    String s = "test";

    switch (s) {

    case "test":

       System.out.println("test");

       break;

    case "test1":

       System.out.println("test1");

       break;

    default:

       System.out.println("break");

       break;

    }

}

 

2:”<>”的運用List tempList = new ArrayList<>(); 即泛型實例化類型自動推斷。

public static void testFX() {

    // JDK 7之前

    List<String> lst1 = new ArrayList<String>();

    for (String item : lst1) {

        System.out.println(item);

    }

    // JDK 7 supports limited type inference for generic instance creation

    List<String> lst2 = new ArrayList<>();

    lst1.add("Mon");

    lst1.add("Tue");

    lst2.add("Wed");

    lst2.add("Thu");

    for (String item : lst2) {

        System.out.println(item);

    }

}

 

3:自定義自動關閉類 

以下是jdk7 api中的接口 ,該方法在try-with-resources語句中會被自動調用,用於自動釋放資源。

 

public interface AutoCloseable {

    /**

     * Closes this resource, relinquishing any underlying resources.

     * This method is invoked automatically on objects managed by the

     * try-with-resources statement.

     *

     * <p>While this interface method is declared to throw {@code

     * Exception}, implementers are <em>strongly</em> encouraged to

     * declare concrete implementations of the {@code close} method to

     * throw more specific exceptions, or to throw no exception at all

     * if the close operation cannot fail.

     *

     * <p> Cases where the close operation may fail require careful

     * attention by implementers. It is strongly advised to relinquish

     * the underlying resources and to internally <em>mark</em> the

     * resource as closed, prior to throwing the exception. The {@code

     * close} method is unlikely to be invoked more than once and so

     * this ensures that the resources are released in a timely manner.

     * Furthermore it reduces problems that could arise when the resource

     * wraps, or is wrapped, by another resource.

     *

     * <p><em>Implementers of this interface are also strongly advised

     * to not have the {@code close} method throw {@link

     * InterruptedException}.</em>

     *

     * This exception interacts with a thread's interrupted status,

     * and runtime misbehavior is likely to occur if an {@code

     * InterruptedException} is {@linkplain Throwable#addSuppressed

     * suppressed}.

     *

     * More generally, if it would cause problems for an

     * exception to be suppressed, the {@code AutoCloseable.close}

     * method should not throw it.

     *

     * <p>Note that unlike the {@link java.io.Closeable#close close}

     * method of {@link java.io.Closeable}, this {@code close} method

     * is <em>not</em> required to be idempotent.  In other words,

     * calling this {@code close} method more than once may have some

     * visible side effect, unlike {@code Closeable.close} which is

     * required to have no effect if called more than once.

     *

     * However, implementers of this interface are strongly encouraged

     * to make their {@code close} methods idempotent.

     *

     * @throws Exception if this resource cannot be closed

     */

    void close() throws Exception;

}

只要實現該接口,在該類對象銷燬時自動調用close方法,你可以在close方法關閉你想關閉的資源,例子如下

public class TestAutoClose implements AutoCloseable {

   

    public void eat() {

       System.out.println("call eat() method.........");

    }

 

    @Override

    public void close() throws Exception {

       System.out.println("call close() method.........");

    }

}

public static void testClose() throws Exception {

    try (TestAutoClose test = new TestAutoClose();) {

       test.eat();

    } catch (Exception ex) {

       ex.printStackTrace();

    }

    // test.close();

}

 

 

這個特性一般用於 IO,如InputStream,Writes,Sockets,Sql classes等。這個新的語言特性允許try語句本身申請更多的資源,這些資源作用於try代碼塊,並自動關閉。

以前的寫法:

BufferedReader br = new BufferedReader(new FileReader(path));

try {

    return br.readLine();

  } finally {

          br.close();

  }

現在:

try (BufferedReader br = new BufferedReader(new FileReader(path)) {

     return br.readLine();

 }

 

 

4:在try catch異常撲捉中,一個catch可以寫多個異常類型,用”|”隔開**

    public static void testClose() throws Exception {

       try (TestAutoClose test = new TestAutoClose();) {

           test.eat();

       } catch (IOException | ClassNotFoundException ex) {

           ex.printStackTrace();

       }

    }

 

JDK 8新特性

參考這裏http://www.runoob.com/java/java8-new-features.html

 

Java 8 (又稱爲 jdk 1.8) 是 Java 語言開發的一個主要版本。 Oracle 公司於 2014 年 3 月 18 日發佈 Java 8 ,它支持函數式編程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。

Java8 新增了非常多的特性,我們主要討論以下幾個:

  • Lambda 表達式 − Lambda允許把函數作爲一個方法的參數(函數作爲參數傳遞進方法中。
  • 方法引用 − 方法引用提供了非常有用的語法,可以直接引用已有Java類或對象(實例)的方法或構造器。與lambda聯合使用,方法引用可以使語言的構造更緊湊簡潔,減少冗餘代碼。
  • 默認方法 − 默認方法就是一個在接口裏面有了一個實現的方法。
  • 新工具 − 新的編譯工具,如:Nashorn引擎 jjs、 類依賴分析器jdeps。
  • Stream API −新添加的Stream API(java.util.stream) 把真正的函數式編程風格引入到Java中。
  • Date Time API − 加強對日期與時間的處理。
  • Optional  − Optional 類已經成爲 Java 8 類庫的一部分,用來解決空指針異常。
  • Nashorn, JavaScript 引擎 − Java 8提供了一個新的Nashorn javascript引擎,它允許我們在JVM上運行特定的javascript應用。

 

Lambda表達式

Lambda表達式是Java SE 8中一個重要的新特性。lambda表達式允許你通過表達式來代替功能接口。 lambda表達式就和方法一樣,它提供了一個正常的參數列表和一個使用這些參數的主體(body,可以是一個表達式或一個代碼塊)。

Lambda 允許把函數作爲一個方法的參數(函數作爲參數傳遞進方法中)。

使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。

 

1.什麼是Lambda?

我們知道,對於一個Java變量,我們可以賦給其一個“值”。 
 

 

函數式接口

函數式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。

函數式接口可以被隱式轉換爲 lambda 表達式。

Lambda 表達式和方法引用(實際上也可認爲是Lambda表達式)上。

如定義了一個函數式接口如下:

 

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

那麼就可以使用Lambda表達式來表示該接口的一個實現(注:JAVA 8 之前一般是用匿名類實現的)

GreetingService greetService1 = message -> System.out.println("Hello " + message);

 

默認方法

Java 8 新增了接口的默認方法。

簡單說,默認方法就是接口可以有實現方法,而且不需要實現類去實現其方法。

我們只需在方法名前面加個 default 關鍵字即可實現默認方法。

爲什麼要有這個特性?

首先,之前的接口是個雙刃劍,好處是面向抽象而不是面向具體編程,

缺陷是,當需要新增接口時候,需要修改全部實現該接口的類,目前的 java 8 之前的集合框架沒有 foreach 方法,通常能想到的解決辦法是在JDK裏給相關的接口添加新的方法及實現。然而,對於已經發布的版本,是沒法在給接口添加新方法的同時不影響已有的實現。所以引進的默認方法。他們的目的是爲了解決接口的修改與現有的實現不兼容的問題。

 

語法:

 

public interface Animal {

    void eat();

    default void eatmouse() {

       System.out.println("吃老鼠");

    }

}

public class Cat implements Animal {

 

    @Override

    public void eat() {

    }

 

    @Override

    public void eatmouse() {

       Animal.super.eatmouse();

    }

 

}

public class Dog implements Animal {

    @Override

    public void eat() {

    }

}

 

 

Stream

Stream 作爲 Java 8 的一大亮點,它與 java.io 包裏的 InputStream 和 OutputStream 是完全不同的概念。它也不同於 StAX 對 XML 解析的 Stream,也不是 Amazon Kinesis 對大數據實時處理的 Stream。Java 8 中的 Stream 是對集合(Collection)對象功能的增強,它專注於對集合對象進行各種非常便利、高效的聚合操作(aggregate operation),或者大批量數據操作 (bulk data operation)。Stream API 藉助於同樣新出現的 Lambda 表達式,極大的提高編程效率和程序可讀性。同時它提供串行和並行兩種模式進行匯聚操作,併發模式能夠充分利用多核處理器的優勢,使用 fork/join 並行方式來拆分任務和加速處理過程。

 

Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次後即用盡了,就好比流水從面前流過,一去不復返。

 

而和迭代器又不同的是,Stream 可以並行化操作,迭代器只能命令式地、串行化操作。顧名思義,當使用串行方式去遍歷時,每個 item 讀完後再讀下一個 item。而使用並行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然後將結果一起輸出。

 

什麼是 Stream

Stream(流)是一個來自數據源的元素隊列並支持聚合操作

  1. 元素是特定類型的對象,形成一個隊列。 Java中的Stream並不會存儲元素,而是按需計算。
  2. 數據源 流的來源。 可以是集合,數組,I/O channel, 產生器generator 等。
  3. 聚合操作 類似SQL語句一樣的操作, 比如filter, map, reduce, find, match, sorted等。

 

和以前的Collection操作不同, Stream操作還有兩個基礎的特徵:

  1. Pipelining: 中間操作都會返回流對象本身。 這樣多個操作可以串聯成一個管道, 如同流式風格(fluent style)。 這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路( short-circuiting)。
  2. 內部迭代: 以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代。 Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。

 

流的構成

當我們使用一個流的時候,通常包括三個基本步驟:

獲取一個數據源(source數據轉換執行操作獲取想要的結果,每次轉換原有 Stream 對象不改變,返回一個新的 Stream 對象(可以有多次轉換),這就允許對其操作可以像鏈條一樣排列,變成一個管道,如下圖所示。

 

 

flatMap

flatMap 把 input Stream 中的層級結構扁平化,就是將最底層元素抽出來放到一起,最終 output 的新 Stream 裏面已經沒有 List 了,都是直接的數字。

    /**

    * @Title: testFlatMap

    * @author: lance.lan

    * @Description: 結果:[1,2,3,4,5,6]

    * @return void

    */

    private static void testFlatMap() {

       Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));

       Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());

       System.out.println(JSONObject.toJSONString(outputStream.toArray()));

    }

 

 

 

filter

filter 方法用於通過設置的條件過濾出元素。以下代碼片段使用 filter 方法過濾出空字符串:

    private static void testFilter() {

       List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

       // 獲取空字符串的數量

       //2

       long count = strings.stream().filter(string -> string.isEmpty()).count();

       System.out.println(count);

       //留下長度大於3

       //["abcd"]

       List<String> finalStr = strings.stream().filter(string -> string.length()>3).collect(Collectors.toList());

       System.out.println(JSONObject.toJSONString(finalStr));

    }

 

 

limit-skip

limit 返回 Stream 的前面 n 個元素;skip 則是扔掉前 n 個元素(它是由一個叫 subStream 的方法改名而來)

/**

* @Title: testLimitAndSkip

* @author: lance.lan

* @Description: limit 返回 Stream 的前面 n 個元素;skip 則是扔掉前 n 個元素

* @return void

*/

public static void testLimitAndSkip() {

    List<Person> persons = new ArrayList<>();

    for (int i = 1; i <= 10000; i++) {

       Person person = new Person("name" + i, i);

       persons.add(person);

    }

    List<String> personList2 = persons.stream().map(Person::getName).limit(10).skip(3).collect(Collectors.toList());

    //[name4, name5, name6, name7, name8, name9, name10]

    //這是一個有 10000 個元素的 Stream,但在 short-circuiting 操作 limit skip 的作用下,

    //管道中 map 操作指定的 getName() 方法的執行次數爲 limit 所限定的 10 次,而最終返回結果在跳過前 3 個元素後只有後面 7 個返回。

    //也就是getName執行了7. 也就是這個map循環執行了7

    System.out.println(personList2);

}

 

 

limit 和 skip 對 sorted 後的運行次數無影響

有一種情況是 limit/skip 無法達到 short-circuiting 目的的,就是把它們放在 Stream 的排序操作後,原因跟 sorted 這個 intermediate 操作有關:此時系統並不知道 Stream 排序後的次序如何,所以 sorted 中的操作看上去就像完全沒有被 limit 或者 skip 一樣。

public static void testLimitAndSkip() {

       List<Person> persons = new ArrayList();

        for (int i = 1; i <= 5; i++) {

        Person person = new Person(i, "name" + i);

        persons.add(person);

        }

       List<Person> personList2 = persons.stream().sorted((p1, p2) ->

       p1.getName().compareTo(p2.getName())).limit(2).collect(Collectors.toList());

       System.out.println(personList2);

    }

 

 

 

sorted

sorted 方法用於對流進行排序。以下代碼片段使用 sorted 方法對輸出的 10 個隨機數進行排序:

public static void testSort() {

    //簡單排序

    Random random = new Random();

    random.ints().limit(10).sorted().forEach(System.out::println);

    //對象排序

    List<Person> persons = new ArrayList<>();

    for (int i = 1; i <= 5; i++) {

       Person person = new Person("name" + i, i);

       persons.add(person);

    }

    List<Person> personList2 = persons.stream().sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).limit(2)

           .collect(Collectors.toList());

    // [{"age":1,"name":"name1"},{"age":2,"name":"name2"}]

    // 這裏會先對5個元素進行排序,在limit操作。

    // testLimitAndSkip有區別

    System.out.println(JSONObject.toJSONString(personList2));

}

 

min/max/distinct

public static void testDistinct() {

    List<String> list = Arrays.asList("a", "b", "c", "a", "c");

    list = list.stream().distinct().collect(Collectors.toList());

    //[a, b, c]

    System.out.println(list);

}

 

public static void testMax() {

    List<String> list = Arrays.asList("ab", "bbb", "cbbb", "a", "c");

    int longest = list.stream().mapToInt(item -> item.length()).max().getAsInt();

    //4

}  

 

Match

Stream 有三個 match 方法,從語義上說:

  1. allMatch:Stream 中全部元素符合傳入的 predicate,返回 true
  2. anyMatch:Stream 中只要有一個元素符合傳入的 predicate,返回 true
  3. noneMatch:Stream 中沒有一個元素符合傳入的 predicate,返回 true

它們都不是要遍歷全部元素才能返回結果。例如 allMatch 只要一個元素不滿足條件,就 skip 剩下的所有元素,返回 false。

public static void testMatch() {

    List<Person> persons = new ArrayList<>();

    persons.add(new Person("name" + 1, 10));

    persons.add(new Person("name" + 2, 21));

    persons.add(new Person( "name" + 3, 34));

    persons.add(new Person( "name" + 4, 6));

    persons.add(new Person( "name" + 5, 55));

    boolean isAllAdult = persons.stream().allMatch(p -> p.getAge() > 18);

    System.out.println("All are adult? " + isAllAdult);

    boolean isThereAnyChild = persons.stream().anyMatch(p -> p.getAge() < 12);

    System.out.println("Any child? " + isThereAnyChild);

}

 

 

Collectors

Collectors 類實現了很多歸約操作,例如將流轉換成集合和聚合元素。Collectors 可用於返回列表或字符串:

private static void testCollectors() {

    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

    List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

     

    System.out.println("篩選列表: " + filtered);

    String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));

    System.out.println("合併字符串: " + mergedString);

}

篩選列表: [abc, bc, efg, abcd, jkl]

合併字符串: abc, bc, efg, abcd, jkl

 

統計

另外,一些產生統計結果的收集器也非常有用。它們主要用於int、double、long等基本類型上,它們可以用來產生類似如下的統計結果。

private static void tongji() {

    List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

     

    IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();

     

    System.out.println("列表中最大的數 : " + stats.getMax());

    System.out.println("列表中最小的數 : " + stats.getMin());

    System.out.println("所有數之和 : " + stats.getSum());

    System.out.println("平均數 : " + stats.getAverage());

}

列表中最大的數 : 7

列表中最小的數 : 2

所有數之和 : 25

平均數 : 3.5714285714285716

 

toArray

private static void testToArray() {

    List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

    Object[] numberObj = numbers.stream().toArray();

    Integer[] numberStr = numbers.stream().toArray(Integer[] :: new);

}

 

結束語

總之,Stream 的特性可以歸納爲:

  • 不是數據結構
  • 它沒有內部存儲,它只是用操作管道從 source(數據結構、數組、generator functionIO channel)抓取數據。
  • 它也絕不修改自己所封裝的底層數據結構的數據。例如 Stream filter 操作會產生一個不包含被過濾元素的新 Stream,而不是從 source 刪除那些元素。
  • 所有 Stream 的操作必須以 lambda 表達式爲參數
  • 不支持索引訪問
  • 你可以請求第一個元素,但無法請求第二個,第三個,或最後一個。不過請參閱下一項。
  • 很容易生成數組或者 List
  • 惰性化
  • 很多 Stream 操作是向後延遲的,一直到它弄清楚了最後需要多少數據纔會開始。
  • Intermediate 操作永遠是惰性化的。
  • 並行能力
  • 當一個 Stream 是並行化的,就不需要再寫多線程代碼,所有對它的操作會自動並行進行的。
  • 可以是無限的
    • 集合有固定大小,Stream 則不必。limit(n) findFirst() 這類的 short-circuiting 操作可以對無限的 Stream 進行運算並很快完成。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章