java怎么實(shí)現(xiàn)流

這篇文章主要介紹“java怎么實(shí)現(xiàn)流”,在日常操作中,相信很多人在java怎么實(shí)現(xiàn)流問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”java怎么實(shí)現(xiàn)流”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

10年專注成都網(wǎng)站制作,成都定制網(wǎng)站,個(gè)人網(wǎng)站制作服務(wù),為大家分享網(wǎng)站制作知識(shí)、方案,網(wǎng)站設(shè)計(jì)流程、步驟,成功服務(wù)上千家企業(yè)。為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁設(shè)計(jì)及定制高端網(wǎng)站建設(shè)服務(wù),專注于成都定制網(wǎng)站,高端網(wǎng)頁制作,對宣傳片制作等多個(gè)領(lǐng)域,擁有豐富的網(wǎng)站維護(hù)經(jīng)驗(yàn)。

引入流

集合是Java中使用最多的API。要是沒有集合,還能做什么呢?幾乎每個(gè)Java應(yīng)用程序都會(huì)制
造和處理集合。集合對于很多編程任務(wù)來說都是非?;镜模核鼈兛梢宰屇惆褦?shù)據(jù)分組并加以處
理。

  • 很多業(yè)務(wù)邏輯都涉及類似于數(shù)據(jù)庫的操作,比如對幾道菜按照類別進(jìn)行分組 (比如全素
    菜肴),或查找出最貴的菜。你自己用迭代器重新實(shí)現(xiàn)過這些操作多少遍?大部分?jǐn)?shù)據(jù)庫
    都允許你聲明式地指定這些操作。比如,以下SQL查詢語句就可以選出熱量較低的菜肴名
    稱: SELECT name FROM dishes WHERE calorie < 400 。你看,你不需要實(shí)現(xiàn)如何
    根據(jù)菜肴的屬性進(jìn)行篩選(比如利用迭代器和累加器),你只需要表達(dá)你想要什么。這個(gè)
    基本的思路意味著,你用不著擔(dān)心怎么去顯式地實(shí)現(xiàn)這些查詢語句——都替你辦好了!
    怎么到了集合這里就不能這樣了呢?

  • 要是要處理大量元素又該怎么辦呢?為了提高性能,你需要并行處理,并利用多核架構(gòu)。
    但寫并行代碼比用迭代器還要復(fù)雜,而且調(diào)試起來也夠受的!

那Java語言的設(shè)計(jì)者能做些什么,來幫助你節(jié)約寶貴的時(shí)間,讓你這個(gè)程序員活得輕松一點(diǎn)兒呢?你可能已經(jīng)猜到了,答案就是流。

流 是什么

流(Stream)是javaAPI的新成員,它允許你以聲明性方式處理數(shù)據(jù)集(通過查詢語句來表達(dá)而不是臨時(shí)編寫一個(gè)實(shí)現(xiàn)).
此外,流還可以并行的進(jìn)行處理,你無須寫任何多線程代碼了.

首先,我們以一個(gè)例子看下流的使用:下面兩段代碼都是用來返回低熱量的菜肴名稱的,
并按照卡路里排序,一個(gè)是用Java 7寫的,另一個(gè)是用Java 8的流寫的。比較一下。不用太擔(dān)心
Java 8代碼怎么寫,我們在接下來的幾節(jié)里會(huì)詳細(xì)解釋。

實(shí)體類 Dish.java

public class Dish

    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;

    //get/set 略
  • java7中實(shí)現(xiàn)

/**
     * java7
     */
    @Test
    public void test1() {

        //選出低熱量菜肴
        ArrayList<Dish> lowCaloricDishes = new ArrayList<>();
        for (Dish dish : menu) {
            if (dish.getCalories() < 400) {
                lowCaloricDishes.add(dish);

            }
        }
        //按照卡路里進(jìn)行排序
        Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
            @Override
            public int compare(Dish dish2, Dish dish3) {
                return Integer.compare(dish2.getCalories(), dish3.getCalories());
            }
        });
        //輸出菜肴名稱
        ArrayList<String> lowCaloricDishesName = new ArrayList<>();
        for

在這段代碼中,你用了一個(gè)“垃圾變量” lowCaloricDishes 。它唯一的作用就是作為一次
性的中間容器。在Java 8中,實(shí)現(xiàn)的細(xì)節(jié)被放在它本該歸屬的庫里了。

  • 之后(Java 8)

/**
     * java8
     */
    @Test
    public void test2() {
        List<String> lowCaloricDishesName =
                menu.stream()
                        //選出低熱量菜肴
                        .filter(d -> d.getCalories() < 400)
                        //按照卡路里進(jìn)行排序
                        .sorted(Comparator.comparing(Dish::getCalories))
                        //輸出菜肴名稱

為了利用多核架構(gòu)并行執(zhí)行這段代碼,你只需要把 stream() 換成 parallelStream() :

/**
     * java8多核架構(gòu)并行執(zhí)行這段代碼
     */
    @Test
    public void test3() {
        List<String> lowCaloricDishesName =
                menu.parallelStream()
                        //選出低熱量菜肴
                        .filter(d -> d.getCalories() < 400)
                        //按照卡路里進(jìn)行排序
                        .sorted(Comparator.comparing(Dish::getCalories))
                        //輸出菜肴名稱

你可能會(huì)想,在調(diào)用 parallelStream 方法的時(shí)候到底發(fā)生了什么。用了多少個(gè)線程?對性
能有多大提升?后面會(huì)詳細(xì)討論這些問題

現(xiàn)在,你可以看出,從軟件工程師的角度來看,新
的方法有幾個(gè)顯而易見的好處:

  • 代碼是以聲明性方式寫的:說明想要完成什么,而不是說明如何實(shí)現(xiàn)一個(gè)操作(利用for if 等控制語句).這種方法加上行為參數(shù)化,可以讓你很輕松的應(yīng)對變化的需求,你很容易再創(chuàng)建一個(gè)代碼版本,利用
    Lambda表達(dá)式來篩選高卡路里的菜肴,而用不著去復(fù)制粘貼代碼

  • 你可以把幾個(gè)基礎(chǔ)操作連接起來:來表達(dá)復(fù)雜的數(shù)據(jù)流水線工作,同時(shí)保證代碼清晰可讀.filter 的結(jié)果被傳給了 sorted 方法,再傳給 map 方法,最后傳給 collect 方法。

需要注意的是: filter(),sorted(),map(), 返回的都是流(Stream),都是Stream的方法,collect()方法除外.關(guān)于jdk8的api可以下載此文檔[jdk8api](jdk api 1.8_google.CHM)

流簡介

java8中的集合支持一個(gè)新的stream()方法,它會(huì)返回一個(gè)流,接口定義在 java.util.stream.Stream中.

那么,流到底是什么呢?簡短的定義就是“從支持?jǐn)?shù)據(jù)處理操作的源生成的元素序列”。讓我們一步步剖析這個(gè)定義:

  • 元素序列: 就像集合一樣,劉燁提供了一個(gè)接口,可以訪問特定元素類型的一組有序值.因?yàn)榧鲜菙?shù)據(jù)結(jié)構(gòu),所以它的主要目的是以特定的時(shí)間/空間復(fù)雜度來存儲(chǔ)訪問元素.但流的目的在于表達(dá)計(jì)算.

  • 源: 流會(huì)使用一個(gè)提供數(shù)據(jù)的源,這些源可以是 數(shù)組,集合,或輸入輸出資源.注意:從有序結(jié)合生成的流會(huì)保留原有的順序,由列表生成的流,其元素順序也與列表一致.

  • 數(shù)據(jù)處理操作: 流的數(shù)據(jù)處理功能類似于數(shù)據(jù)庫的操作.以及函數(shù)式編程語言的常用操作.如 filter 、 map 、 reduce 、 find 、 match 、 sort 等。流操作可以順序執(zhí)行,也可并行執(zhí)行。
    此外,流操作有兩個(gè)重要的特點(diǎn)。

  • 流水線: 很多流操作本身會(huì)返回一個(gè)流.這樣多個(gè)操作就可以連接起來形成一個(gè)更大的流水線.流水線操作可以看成對數(shù)據(jù)源進(jìn)行數(shù)據(jù)庫式查詢.

  • 內(nèi)部迭代: 與使用迭代器對集合進(jìn)行顯示迭代不同,流的迭代都是在背后進(jìn)行的.

讓我們來看一段能夠體現(xiàn)所有這些概念的代碼:

@Test
    public void test4() {
        List<String> lowCaloricDishesName =
                //1.從 menu 獲得流(菜肴列表),建立操作流水線
                menu.parallelStream()
                        //2.選出高熱量菜肴
                        .filter(d -> d.getCalories() > 300)
                        //3.輸出菜肴名稱
                        .map(Dish::getName)
                        //4.只選擇前三個(gè)
                        .limit(3)
                        //5.將結(jié)果保存在另一個(gè)List中

在本例中,我們顯示對menu進(jìn)行stream操作,得到一個(gè)流,數(shù)據(jù)源是菜肴列表menu,接下來對流進(jìn)行一系列數(shù)據(jù)處理操作:filter 、 map 、 limit
和 collect 。除了 collect 之外,所有這些操作都會(huì)返回另一個(gè)流,這樣它們就可以接成一條流水線,于是就可以看作對源的一個(gè)查詢.
最后collect開始處理流水線,并返回一個(gè)結(jié)果(collect和別的操作不一樣,它返回的不是一個(gè)流而是一個(gè)list).
在調(diào)用collect之前,沒有任何結(jié)果產(chǎn)生,事實(shí)上,根本就沒有從menu里選擇元素.你可以這么理解:鏈中的方法調(diào)用都在排隊(duì)等待,直到調(diào)用 collect

圖4-2顯示了流操作的順序: filter 、 map 、 limit 、 collect ,

每個(gè)操作簡介如下。

在進(jìn)一步介紹能對流做什么操作之前,先讓我們回過頭來看看Collection API和新的Stream
API的思想有何不同

流與集合

粗略的講,流與集合的差異就在于什么時(shí)候進(jìn)行計(jì)算,集合是內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),它包含數(shù)據(jù)結(jié)構(gòu)中目前所有的值(結(jié)合中每個(gè)元素必須先計(jì)算出來才能添加到集合中.)
(你可以往集合里加?xùn)|西或者刪東西,但是不管什么時(shí)候,集合中的每個(gè)元素都是放在內(nèi)存里的,元素都得先算出來才能成為集合的一部分。)

相比之下,流是再概念上固定的數(shù)據(jù)結(jié)構(gòu).這個(gè)思想就是用戶僅僅從流中提取需要的值,而這些值,在用戶看不見的地方,只會(huì)按需生成.
這是一種 生產(chǎn)者–消費(fèi)者 的關(guān)系,從另一個(gè)角度來說,流就想一個(gè)延遲創(chuàng)建的集合:只有在消費(fèi)者要求的時(shí)候才會(huì)計(jì)算值(用管理學(xué)的話說這就是需求驅(qū)動(dòng),甚至是實(shí)時(shí)制造)。

與此相反,集合則是急切創(chuàng)建的(供應(yīng)商驅(qū)動(dòng):先把倉庫裝滿,再開始賣,就像那些曇花一
現(xiàn)的圣誕新玩意兒一樣)。以質(zhì)數(shù)為例,要是想創(chuàng)建一個(gè)包含所有質(zhì)數(shù)的集合,那這個(gè)程序算起
來就沒完沒了了,因?yàn)榭傆行碌馁|(zhì)數(shù)要算,然后把它加到集合里面。當(dāng)然這個(gè)集合是永遠(yuǎn)也創(chuàng)建
不完的,消費(fèi)者這輩子都見不著了.

圖4-3用DVD對比在線流媒體的例子展示了流和集合之間的差異

只能遍歷一次

請注意,和迭代器一樣,流只能遍歷一次,遍歷完之后,我們就說這個(gè)流已經(jīng)被消費(fèi)掉了,
你可以從原始數(shù)據(jù)源那里再獲得一個(gè)新的流來重新遍歷一遍,就像迭代器一樣(這里假設(shè)它是集
合之類的可重復(fù)的源,如果是I/O通道就沒戲了)。例如,以下代碼會(huì)拋出一個(gè)異常,說流已被消
費(fèi)掉了:java.lang.IllegalStateException: stream has already been operated upon or closed

@Test
    public void test5(){

        List<String> title = Arrays.asList("Java8", "In", "Action");
        Stream<String> stream = title.stream();
        stream.forEach(System.out::println);
        stream.forEach(System.out::println);
    }

所以要記得,流只能消費(fèi)一次!

外部迭代與內(nèi)部迭代

內(nèi)部迭代時(shí),項(xiàng)目可以透明地并行處理,或者用更優(yōu)化的順
序進(jìn)行處理。要是用Java過去的那種外部迭代方法,這些優(yōu)化都是很困難的。這似乎有點(diǎn)兒雞蛋
里挑骨頭,但這差不多就是Java 8引入流的理由了——Streams庫的內(nèi)部迭代可以自動(dòng)選擇一種適
合你硬件的數(shù)據(jù)表示和并行實(shí)現(xiàn)。

流操作

java.util.stream.Stream 中的 Stream 接口定義了許多操作。它們可以分為兩大類.
可以連接起來的流操作稱為中間操作,關(guān)閉流的操作稱為終端操作

中間操作

注入filter和sorted等中間操作會(huì)返回另外一個(gè)流,這讓多個(gè)中間操作可以連接起來形成一個(gè)查詢.重要的是:
除非流水線上觸發(fā)一個(gè)終端操作,否則中間操作不會(huì)執(zhí)行任何一個(gè)處理–他們很懶,這是因?yàn)橹虚g操作一般都可以合并起來,在終端操作時(shí)一次性全部處理

為了搞清楚流水線中到底發(fā)生了什么,我們把代碼改一改,讓每個(gè)Lambda都打印出當(dāng)前處
理的菜肴:

@Test
    public void test6() {

        List<String> names =
                //1.從 menu 獲得流(菜肴列表),建立操作流水線
                menu.stream()
                        //2.選出高熱量菜肴
                        .filter(d -> {
                            System.out.println("filtering" + d.getName());
                            return d.getCalories() > 300;
                        })
                        //3.輸出菜肴名稱
                        .map(d -> {
                            System.out.println("mapping" + d.getName());
                            return d.getName();
                        })
                        //4.只選擇前三個(gè)
                        .limit(3)
                        //5.將結(jié)果保存在另一個(gè)List中

此代碼執(zhí)行時(shí)將打印:

filteringpork
mappingpork
filteringbeef
mappingbeef
filteringchicken
mappingchicken
[pork, beef, chicken]

你會(huì)驚訝的發(fā)現(xiàn),流操作并沒有打印出我們預(yù)期的信息.這是因?yàn)橛泻脦追N優(yōu)化利用了流的延遲性質(zhì).

  • 第一:盡管很多菜的熱量都高于300卡路里,但只選出了前三個(gè)!這是因?yàn)?limit 操作和一種稱為短路的技巧

  • 第二,盡管 filter 和 map 是兩個(gè)獨(dú)立的操作,但它們合并到同一次遍歷中了(我們把這種技術(shù)叫作循環(huán)合并)。

終端操作

終端操作會(huì)從流的流水線生成結(jié)果.其結(jié)果是任何不是流的值,比如List,Integer,甚至是void.

例如在下面的流水線中,foerach是一個(gè)返回void的終端操縱,它會(huì)對源中的每道菜應(yīng)用一個(gè)Lambda,把 System.out.println 傳遞給 forEach ,并要求它打印出由 menu 生成的流中的每一個(gè) Dish :

menu.stream().forEach(System.out::println);

到此,關(guān)于“java怎么實(shí)現(xiàn)流”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

當(dāng)前文章:java怎么實(shí)現(xiàn)流
文章地址:http://bm7419.com/article6/gejpig.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司網(wǎng)站內(nèi)鏈、面包屑導(dǎo)航、微信小程序、網(wǎng)頁設(shè)計(jì)公司、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)