Java8中的Stream相關(guān)用法

概念

? ? ? ?在Java中有關(guān)流的概念有很多哟冬,比如輸入輸出流(InputStream/OutputStream)智嚷,或者在對(duì)XML文件進(jìn)行SAX解析的流咖摹,甚至于大數(shù)據(jù)的實(shí)時(shí)處理也有流( Amazon Kinesis ),在Java8版本之后甥雕,JDK又引入了一個(gè)全新的Stream API踩身,它不是前面說(shuō)的任何一種,而是對(duì)集合對(duì)象功能的增強(qiáng)社露,同時(shí)結(jié)合Java8以后引入的Lambda表達(dá)式挟阻,可以極大的簡(jiǎn)化編碼量,而且程序的可讀性也會(huì)有一個(gè)很大的提升峭弟。

? ? ? ?Stream不僅僅只是增強(qiáng)了Java中集合的操作附鸽,同時(shí)它也是非常高效的,它提供了串行和并行的兩種模式瞒瘸,現(xiàn)在的計(jì)算機(jī)都是多核的時(shí)代坷备,可以充分利用這一特點(diǎn)提高程序執(zhí)行效率,暫時(shí)不用深究串行和并行的問(wèn)題情臭,后面會(huì)介紹到省撑,只要知道,引入了Stream API之后俯在,如果需要改成并行執(zhí)行竟秫,主體代碼完全不用動(dòng),只要切換成并行模式就行跷乐,非常方便快捷肥败。

什么是Stream?

? ? ? ?前面介紹了半天,其實(shí)如果沒(méi)有用過(guò)的人馒稍,第一眼看上去皿哨,都是模糊不清的,我們可以參考現(xiàn)實(shí)生活中的場(chǎng)景纽谒,比如工廠的流水線往史,無(wú)論是自動(dòng)化還是人工化的流水線操作,流水線的作業(yè)特點(diǎn)很明顯佛舱,一般都是在不同的流水節(jié)點(diǎn)配置不同的人員或者自動(dòng)化機(jī)器。比如:在生產(chǎn)藥品的過(guò)程中挨决,如果藥片生產(chǎn)完成后请祖,一般都需要一些檢驗(yàn)以及包裝的過(guò)程,這時(shí)一般都會(huì)有一條流水作業(yè)脖祈,在這個(gè)流水作業(yè)過(guò)程中肆捕,有負(fù)責(zé)檢測(cè)的,有負(fù)責(zé)包裝的盖高,等到整個(gè)流水線全部走完慎陵,最后就會(huì)變成規(guī)格相等的成品包裝藥。

? ? ? ?而Stream API引入后喻奥,我們針對(duì)集合的操作就有點(diǎn)類似于這種流水線的作業(yè)一樣席纽,對(duì)于集合中的元素,就相當(dāng)于一顆顆藥片撞蚕,在經(jīng)歷了篩選润梯,包裝之后,變成另外一種“規(guī)格”呈現(xiàn)出來(lái)甥厦,而流在經(jīng)歷了終結(jié)操作之后纺铭,會(huì)依據(jù)源數(shù)據(jù)生成一個(gè)我們需要的最終數(shù)據(jù)結(jié)構(gòu)。當(dāng)然這里類比流水線也并不是特別貼切刀疙,但是大致的概念的上還是很像的舶赔,便于快速了解Java8中這套新引入的Stream。

? ? ? ?Stream不是集合元素谦秧,更不是數(shù)據(jù)結(jié)構(gòu)竟纳,它跟數(shù)據(jù)的存儲(chǔ)沒(méi)有任何關(guān)系,它只是一種針對(duì)數(shù)據(jù)的計(jì)算而存在的油够,可以把它看成是更高級(jí)的迭代器蚁袭。回想一下石咬,我們傳統(tǒng)的在進(jìn)行集合操作的時(shí)候揩悄,例如:過(guò)濾掉集合中的某些元素,或者對(duì)集合中的數(shù)據(jù)進(jìn)行再次加工鬼悠,一般我們都需要不停迭代集合內(nèi)部的元素删性,然后進(jìn)行條件判斷亏娜,通過(guò)條件的留下,不符合的篩掉蹬挺,這種代碼寫(xiě)出來(lái)一般都大同小異维贺,而且代碼量都很大,閱讀起來(lái)也比較費(fèi)事巴帮。Stream可以解決這些問(wèn)題溯泣,我們只要給出針對(duì)集合內(nèi)部元素需要進(jìn)行的操作,Stream 會(huì)隱式地在內(nèi)部進(jìn)行遍歷榕茧,然后進(jìn)行相應(yīng)的數(shù)據(jù)轉(zhuǎn)換垃沦。

? ? ? ?而且類似于流水線一樣,Stream是單向的用押,不可重復(fù)的肢簿,只能遍歷一次,就像流水線作業(yè)一樣蜻拨,只能一條路走到頭池充,走完之后就無(wú)法再重新走一遍了。

? ? ? ?前面說(shuō)過(guò)Stream是可以進(jìn)行并行化模式的缎讼,這點(diǎn)也是它不同于迭代器的一點(diǎn)谅畅,這是一個(gè)什么概念呢宋光?串行化操作就是對(duì)集合內(nèi)部的數(shù)據(jù)逐個(gè)讀取,但是使用并行化模式后,就會(huì)將數(shù)據(jù)分成多段睛廊,每段數(shù)據(jù)都會(huì)在不同的線程中去處理已艰,然后將結(jié)果一同輸出祟滴,這里的并行操作依賴于Java7中引入的 Fork/Join 框架(JSR166y)來(lái)拆分任務(wù)和加速處理過(guò)程刊懈。這就類似于流水作業(yè)線一樣,會(huì)有多條流水作業(yè)線同時(shí)進(jìn)行產(chǎn)品的流水線操作捷凄,可以加速處理的速度忱详。

Stream的構(gòu)成

? ? ? ?根據(jù)前面的介紹,可以了解到跺涤,Stream中有三個(gè)很重要的步驟:源數(shù)據(jù)(source)匈睁、數(shù)據(jù)轉(zhuǎn)換(transforming values)、執(zhí)行操作(operations)桶错。數(shù)據(jù)源就不需要贅述了航唆,一般就是一個(gè)集合,數(shù)據(jù)轉(zhuǎn)換其實(shí)就是對(duì)集合中的數(shù)據(jù)進(jìn)行一系列的校驗(yàn)院刁,篩選甚至是再加工處理糯钙;執(zhí)行操作就是將符合條件的數(shù)據(jù)整合成需要的數(shù)據(jù)結(jié)構(gòu)返回出來(lái)。而且Stream的很多方法都是返回它自身(this),在編碼時(shí)完全可以采用鏈?zhǔn)骄幊倘伟叮瑢?duì)數(shù)據(jù)的操作就會(huì)像一個(gè)鏈條一樣排列在一起再榄,形成一個(gè)管道。這樣還有個(gè)好處:如果有需要享潜,可以在鏈條之中按照需要插入各個(gè)轉(zhuǎn)換操作困鸥,做成一種類似與“可插拔”的效果。

source生成方式

  • Collection和數(shù)組

    • Collection.stream和parallelStream方法

    • Arrays.stream(T array)和Stream.of()

  • 從BufferReader中生成

    • java.io.BufferReader.lines()
  • 靜態(tài)工廠

    • java.util.stream.IntStream.range()

    • java.nio.file.Files.walk()

  • 自己構(gòu)建

    • java.util.Spliterator
  • 其他方式

    • Random.ints()

    • BitSet.stream()

    • Pattern.splitAsStream(java.lang.CharSequence)

    • JarFile.stream()

Stream操作類型

? ? ? ?流的操作分為兩個(gè)階段剑按,也就是它的兩種類型:Intermediate和terminal疾就,簡(jiǎn)單翻譯過(guò)來(lái)就是“中間”和“最終操作”。結(jié)合前面的介紹 艺蝴,簡(jiǎn)單來(lái)說(shuō)就是:Intermediate操作對(duì)應(yīng)著數(shù)據(jù)轉(zhuǎn)換過(guò)程虐译,一個(gè)Stream可以進(jìn)行多次的Intermediate操作,而且Stream有一個(gè)特點(diǎn)吴趴,就是它的惰性(lazy),具體就體現(xiàn)在:多次的Intermediate操作實(shí)際上并不會(huì)真正遍歷數(shù)據(jù)侮攀,只有在最終的那次Terminal操作后锣枝,才會(huì)循環(huán)Stream里面的集合,然后執(zhí)行所有的操作兰英。所以它前面的Intermediate并不會(huì)真正操作數(shù)據(jù)撇叁。

除此之外,還有一種成為short-circuiting操作畦贸,翻譯過(guò)來(lái)叫短路操作:

  • 對(duì)于一個(gè) intermediate 操作陨闹,如果它接受的是一個(gè)無(wú)限大(infinite/unbounded)的 Stream,但返回一個(gè)有限的新Stream

  • 對(duì)于一個(gè) terminal 操作薄坏,如果它接受的是一個(gè)無(wú)限大的 Stream趋厉,但能在有限的時(shí)間計(jì)算出結(jié)果。

上面的概念看上去也是一臉懵逼胶坠,但是畢竟它是專業(yè)性解釋君账,還是要貼出來(lái)的,通俗點(diǎn)來(lái)說(shuō):

? ? ? ?我們可以聯(lián)想一種場(chǎng)景沈善,比如現(xiàn)在有無(wú)窮的人員信息數(shù)據(jù)乡数,我們現(xiàn)在需要找到其中5個(gè)具有某些特征人員信息,這時(shí)候其實(shí)我們是沒(méi)有必要全部遍歷所有的數(shù)據(jù)闻牡,只是在遍歷過(guò)程中如果發(fā)現(xiàn)符合條件的人净赴,就通過(guò),一旦達(dá)到五條數(shù)據(jù)之后罩润,剩下的就完全可以拋棄了玖翅,這個(gè)有點(diǎn)類似于我們常說(shuō)的邏輯運(yùn)算符(&& 和 || )的短路操作,如:&&操作,如果前面為false烧栋,后面就不會(huì)執(zhí)行了直接返回false写妥, || 也是一樣,如果前面一個(gè)結(jié)果為true审姓,后面也不會(huì)執(zhí)行珍特。所以說(shuō)上面介紹的兩點(diǎn)短路操作情況,其實(shí)說(shuō)得都是一種情況:數(shù)據(jù)源無(wú)限魔吐,結(jié)果有限扎筒,這樣才能在有限時(shí)間內(nèi)得到結(jié)果。

? ? ? ?另外需要明確的是:Stream操作的過(guò)程中酬姆,是不會(huì)對(duì)源數(shù)據(jù)做任何修改的嗜桌,在經(jīng)歷過(guò)Stream處理后的結(jié)果,一般都是存儲(chǔ)到另外的一個(gè)空間中辞色,對(duì)源數(shù)據(jù)沒(méi)有任何影響骨宠。

  • Intermediate常用的方法:map (mapToInt, flatMap 等)、 filter相满、 distinct层亿、 sorted、 peek立美、 limit匿又、 skip、 parallel建蹄、 sequential碌更、 unordered

  • Terminal操作常用的方法:forEach、 forEachOrdered洞慎、 toArray痛单、 reduce、 collect劲腿、 min桦他、 max、 count谆棱、 anyMatch快压、 allMatch、 noneMatch垃瞧、 findFirst蔫劣、 findAny、 iterator

  • Short-circuiting常用操作:anyMatch个从、 allMatch脉幢、 noneMatch歪沃、 findFirst、 findAny嫌松、 limit

? ? ? ?上面這些方法只是作為一個(gè)簡(jiǎn)單記錄沪曙,后面介紹使用的時(shí)候,會(huì)具體用到萎羔,到時(shí)就可以了解它們的功能了液走,這里只是記錄下來(lái),作為一個(gè)了解贾陷。

Stream的使用

? ? ? ?前面扯了這么多缘眶,實(shí)際上仍然沒(méi)有具體說(shuō)怎么使用,有了前面的概念介紹髓废,下面介紹使用的時(shí)候巷懈,就會(huì)清楚多了,不會(huì)像第一次看到那樣毫無(wú)頭緒了慌洪。流的使用其實(shí)就是實(shí)現(xiàn)一個(gè) filter-map-reduce 過(guò)程顶燕,過(guò)程中使用Lambda表達(dá)式,是一種函數(shù)式編程冈爹,對(duì)于函數(shù)式編程概念不清楚的涌攻,可以了解一下。其實(shí)我們?nèi)绻煜s就會(huì)很快上手使用了犯助,這個(gè)跟ES6中遍歷數(shù)組的操作類似。

Stream對(duì)象的構(gòu)建

Stream的構(gòu)建其實(shí)就是前面介紹的source的生成方式中介紹的那樣即可:

//使用Stream.of構(gòu)建
Stream stream = Stream.of("a", "b", "c");
//使用數(shù)組構(gòu)建
String[] array = {"a", "b", "c"};
stream = Arrays.stream(array);
//使用Collection構(gòu)建
List<String> list = Arrays.asList(array);
stream = list.stream();

? ? ? ?Stream是支持泛型的维咸,但是對(duì)于基本數(shù)據(jù)類型和對(duì)應(yīng)的包裝類型剂买,存在自動(dòng)拆裝箱的情況,這個(gè)過(guò)程比較耗費(fèi)性能癌蓖,所以這里Stream提供了IntStream瞬哼、LongStream、DoubleStream這三種特殊的Stream用以基本數(shù)據(jù)類型的計(jì)算租副。目前只有這三種(java8版本)坐慰。

//數(shù)值流的構(gòu)造
IntStream.of(1, 2, 3);
//range和rangeClosed構(gòu)建
//range表示開(kāi)區(qū)間 [1, 3),rangeClosed表示閉區(qū)間 [1, 3]
IntStream.range(1, 3);// 1, 2
IntStream.rangeClosed(1, 3);// 1, 2, 3

Stream具體操作

這里簡(jiǎn)單介紹一些關(guān)于Stream在具體代碼中的使用方式,如果想要了解更多用僧,可以考慮查閱更多的文檔结胀。

//一個(gè)簡(jiǎn)單的場(chǎng)景:字符串集合中,將所有字符串全部轉(zhuǎn)換成大寫(xiě)
String[] words = {'a', 'b', 'c', 'd'};
Stream stream = Arrays.toList(words).stream();
List<String> output = stream.map(String::toUpperCase)
 .collect(Collectors.toList())责循;

? ? ? ?這里使用了“String::toUpperCase”這種寫(xiě)法糟港,這個(gè)也是Java8新引入的特性Supplier,這里就不再深入介紹它了院仿,這里只要知道秸抚,它的功能就是找到String類中定義的toUpperCase方法速和,然后將stream中的每個(gè)元素作為toUpperCase方法的入?yún)ⅲ煌U{(diào)用它并返回新的結(jié)果剥汤。

? ? ? ?上面介紹中颠放,可以看到collect其實(shí)就是一個(gè)Terminal操作,中間的這個(gè)map就是一個(gè)Intermediate操作吭敢,當(dāng)然這個(gè)Intermediate操作還可以繼續(xù)添加碰凶,對(duì)源數(shù)據(jù)繼續(xù)進(jìn)行轉(zhuǎn)換。這里再回頭看前面介紹過(guò)的Intermediate操作常用的方法省有,可以看到痒留,這個(gè)過(guò)程有很多方法可以調(diào)用,比如:filter過(guò)濾用的蠢沿、forEach遍歷用的等等伸头。

? ? ? ?Terminal操作永遠(yuǎn)都是在鏈條的最后,并且只能調(diào)用一次舷蟀,一旦執(zhí)行后恤磷,Stream上的元素就被“消費(fèi)”掉了,無(wú)法對(duì)一個(gè)Stream進(jìn)行兩次Terminal操作野宜。例如:

stream.forEach(element -> doOneThing(element));
stream.forEach(element -> doAnotherThing(element));

? ? ? ?這里的forEach就是一個(gè)Terminal操作扫步,如果確實(shí)有需要對(duì)其中的每一個(gè)數(shù)據(jù)有其他操作,可以添加到Intermediate操作過(guò)程中匈子,這里以peek方法為例:

Stream.of("one","two","three","four","five")
       .filter(e -> e.length() > 3)
       .peek(e -> System.out.println("Filtered value: " + e))
       .map(String::toUpperCase)
       .peek(e -> System.out.println("Mapped value: " + e))
       .collect(Collectors.toList());

? ? ? ?上面的代碼中河胎,Intermediate操作就疊加了很多層,然后collect操作結(jié)束整個(gè)流過(guò)程虎敦,這里也很明顯能感受到Stream編程帶來(lái)的好處:代碼的可讀性大幅度提高了游岳,而且代碼比較優(yōu)雅。我們不論熟不熟悉上面的具體語(yǔ)法其徙,但是通過(guò)閱讀上面的代碼胚迫,我們很明看可以知道它到底是在干什么:對(duì)字符串集合過(guò)濾出長(zhǎng)度大于3的元素,輸出通過(guò)校驗(yàn)的元素唾那,將通過(guò)的元素轉(zhuǎn)成大寫(xiě)访锻,再次輸出結(jié)果,最后返回List<String>闹获,這個(gè)就是可讀性期犬,如果按照傳統(tǒng)的方式,我們需要一遍又一遍迭代遍歷集合避诽,才能達(dá)到上面的效果哭懈,代碼量會(huì)明顯加大,而且代碼可讀性非常差茎用,不夠一目了然遣总。

? ? ? ?仔細(xì)看前面說(shuō)過(guò)的Terminal常用操作方法和Short-circuiting常用操作睬罗,可以發(fā)現(xiàn)里面是有些重疊的,比如:findFirst旭斥、findAll容达、anyMatch等等。這類操作根據(jù)方法名稱就可以了解它們的功能了垂券,這里我就不再贅述了花盐,之所以提一下,主要是需要注意一下:Short-circuiting與其他兩類操作的界限是不明顯的菇爪,這個(gè)仔細(xì)一想也能明白算芯,它們分類的出發(fā)點(diǎn)都不同,所以有重合很正常凳宙。

自定義生成流

? ? ? ?這里我們需要用到Stream.generate或者Stream.iterate方法熙揍,通過(guò)實(shí)現(xiàn) Supplier 接口,你可以自己來(lái)控制流的生成氏涩。把 Supplier 實(shí)例傳遞給 Stream.generate() 生成的 Stream届囚。它默認(rèn)是串行而且無(wú)序的。現(xiàn)在以生成10個(gè)隨機(jī)數(shù)為例:

//傳統(tǒng)方式:借助于Random
Random seed = new Random();
Supplier<Integer> random = seed::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
?
//Stream.generate方式
IntStream.generate(() -> (int) (System.nanoTime() % 100))
 .limit(10)
 .forEach(System.out::println);

? ? ? ?上面代碼中是尖,后者采用了System.nanoTime()產(chǎn)生系統(tǒng)隨機(jī)數(shù)意系,因?yàn)樗菬o(wú)限的,如果不進(jìn)行短路操作饺汹,Stream中會(huì)不斷產(chǎn)生隨機(jī)數(shù)蛔添,沒(méi)有邊界,所以必須要limit一下兜辞,獲取前十個(gè)即可迎瞧。generate方法里面的參數(shù)我們也可以自己手動(dòng)實(shí)現(xiàn),只要寫(xiě)一個(gè)類實(shí)現(xiàn)Supplier即可弦疮,需要什么邏輯在具體實(shí)現(xiàn)類里面寫(xiě)清楚就行夹攒。

class MusicSupplier implements Supplier<Music> {
   private int index = 0;

   @Override
   public Music get() {
     return new Music(index++, "Music_" + index);
   }
}
class Music{
   private int id;
   private String name;
   public Music(int id, String name) {
     this.id = id;
     this.name = name;
   }
   ...省略getter和setter
}
class Test {
   public static void main(String[] args) {
     //生成10個(gè)Music對(duì)象并打印結(jié)果
     Stream.generate(new MusicSupplier())
           .limit(10)
           .forEach(m -> System.out.println(m.getId() + "---" + m.getName()));
   }
}

? ? ? ?下面再來(lái)說(shuō)一下Stream.iterate蜘醋,它其實(shí)跟reduce操作很像胁塞,接受一個(gè)種子值和一個(gè)UnaryOperator(一元操作符,例如函數(shù) f )压语,然后種子值成為 Stream 的第一個(gè)元素啸罢,f(seed) 為第二個(gè),f(f(seed)) 第三個(gè)胎食,以此類推扰才。中學(xué)階段學(xué)到的數(shù)列其實(shí)就可以用這種方式,如:等差數(shù)列厕怜、等比數(shù)列之類的衩匣。

//等差為3的數(shù)列
Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " "));

Collectors 的 reduction 操作

? ? ? ?這里的Collectors是java.util.stream包下的一個(gè)輔助類蕾总,主要是輔助流的輸出結(jié)果的轉(zhuǎn)換,主要說(shuō)的是groupingBy和partitionBy琅捏,reduction翻譯過(guò)來(lái)就是減少生百、還原的意思,其實(shí)就是類似于數(shù)據(jù)庫(kù)中的分組一樣柄延,比如現(xiàn)在有一個(gè)person表蚀浆,我們需要根據(jù)性別分組統(tǒng)計(jì),統(tǒng)計(jì)男女各對(duì)應(yīng)有多少人搜吧。整體來(lái)說(shuō)市俊,它最終的結(jié)果是整合后的結(jié)果。下面有個(gè)示例:隨機(jī)生成100個(gè)Person對(duì)象滤奈,

private static class PersonSupplier implements Supplier<Person> {
   private int index = 0;
   private Random random = new Random();
   @Override
   public Person get() {
     return new Person(index++, "StormTestUser" + index, random.nextInt(100));
   }
}
?
private static class Person {
   public int no;
   private String name;
   private int age;
   public Person (int no, String name, int age) {
     this.no = no;
     this.name = name;
     this.age = age;
   }
   public String getName() {
     System.out.println(name);
     return name;
   }
   public int getAge() {
     return age;
   }
}
?
public class Test{
   public static void main(String[] args) {
     //這里有一個(gè)例子:根據(jù)年齡分組
     Map<Integer, List<Person>> personGroups = 
     Stream.generate(new PersonSupplier())
           .limit(100)
           .collect(Collectors.groupingBy(Person::getAge));
     Iterator it = personGroups.entrySet().iterator();
     while (it.hasNext()) {
       Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next();
       System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
     }

     //按照未成年人和成年人歸組
     Map<Boolean, List<Person>> 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());
   }
}

Stream的優(yōu)勢(shì)

  • 提高代碼可讀性:這個(gè)通過(guò)前面的介紹也應(yīng)該可以有一個(gè)清晰的感受摆昧,代碼的條理性和可讀性都有很大的提升,可讀性高僵刮,維護(hù)起來(lái)就比較方便据忘,軟件主要的生命周期都是在維護(hù)階段,所以代碼的可維護(hù)性以及維護(hù)成本非常重要搞糕。

  • 降低代碼量勇吊,提高靈活度:因?yàn)閭鹘y(tǒng)的集合操作,必定擺脫不了遍歷操作窍仰,而Stream隱式遍歷就大大減少了編碼人員的工作量汉规,我們不需要關(guān)注具體的遍歷情況,只需要將便利過(guò)程中需要加入的邏輯放到 Intermediate 操作中驹吮。而且因?yàn)?Intermediate 操作的可重復(fù)性针史,后期如果需要添加額外的處理邏輯,直接在代碼鏈上添加或刪除就行碟狞,方便靈活啄枕。

  • 無(wú)限數(shù)據(jù)量:理論上,source的數(shù)據(jù)量可以是無(wú)限的族沃,只要有相應(yīng)的短路操作或者能夠快速得到結(jié)果的操作即可频祝,這些在對(duì)于一些海量數(shù)據(jù)的情況下,Stream提供了一種更快捷優(yōu)雅的解決方式脆淹。

  • 支持并行:可以充分利用現(xiàn)代計(jì)算機(jī)多核的優(yōu)勢(shì)常空,極大地提高了數(shù)據(jù)的處理速度。

Stream的使用場(chǎng)景

多個(gè)集合操作

? ? ? ?比如:先進(jìn)行filter過(guò)濾盖溺,然后再forEach漓糙,這時(shí)傳統(tǒng)做法就是先遍歷一次進(jìn)行顧慮,然后再一次遍歷過(guò)濾后的數(shù)據(jù)集合烘嘱,此時(shí)如果使用Stream操作就變得優(yōu)雅簡(jiǎn)單昆禽,并且非常高效蝗蛙。

對(duì)性能要求比較高

? ? ? ?這里就需要提升數(shù)據(jù)的處理速度,比如并行模式醉鳖,這時(shí)傳統(tǒng)的方案需要額外添加許多邏輯歼郭,甚至是并發(fā)的邏輯,但是采用Stream的方式非常簡(jiǎn)單快捷辐棒,如果遇到這種場(chǎng)合病曾,可以考慮Stream的方案。

函數(shù)式編程

? ? ? ?Stream的設(shè)計(jì)初衷之一就是為了在Java中引入函數(shù)式的編程風(fēng)格漾根,如果團(tuán)隊(duì)中確實(shí)有這種偏好或者規(guī)定泰涂,可以考慮使用Stream。

無(wú)界限

? ? ? ?集合的大小是有界的辐怕,但是流不需要逼蒙,有許多短路操作可以允許我們?cè)谟邢薜臅r(shí)間內(nèi)完成無(wú)限流的計(jì)算,如果遇到這種情況寄疏,只能采用Stream方式是牢,因?yàn)閭鹘y(tǒng)方式不能達(dá)到這種效果。

其他問(wèn)題

paralleStream的線程安全問(wèn)題

? ? ? ?Stream支持并行模式陕截,但是如果使用不當(dāng)驳棱,很容易陷入誤區(qū)。這里舉一個(gè)簡(jiǎn)單的例子:分別用串行农曲、并行以及加鎖的方式往三個(gè)list集合中添加一萬(wàn)個(gè)元素:

private static List<Integer> list1 = new ArrayList<>();
private static List<Integer> list2 = new ArrayList<>();
private static List<Integer> list3 = new ArrayList<>();
private static Lock lock = new ReentrantLock();
?
public static void main(String[] args) {
   IntStream.range(0, 10000).forEach(list1::add);
?
   IntStream.range(0, 10000).parallel().forEach(list2::add);
?
   IntStream.range(0, 10000).forEach(i -> {
     lock.lock();
     try {
       list3.add(i);
     }finally {
       lock.unlock();
     }
   });
?
   System.out.println("串行執(zhí)行的大猩缃痢:" + list1.size());
   System.out.println("并行執(zhí)行的大小:" + list2.size());
   System.out.println("加鎖并行執(zhí)行的大腥楣妗:" + list3.size());
}

? ? ? ?串行和加鎖的方式每次得到的結(jié)果都是10000形葬,是正確的,但是中間的并行執(zhí)行每次結(jié)果都不一樣暮的,很明顯笙以,并行模式下,并不能保證線程安全冻辩。針對(duì)這種情況猖腕,它的解決方案是使用collect和reduce接口,深層的原因涉及到Stream的具體原理微猖,這里就不再深入谈息,只是記住一個(gè)結(jié)論:paralleStream里直接去修改變量是非線程安全的缘屹,但是采用collect和reduce操作就是滿足線程安全的了凛剥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市轻姿,隨后出現(xiàn)的幾起案子犁珠,更是在濱河造成了極大的恐慌逻炊,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犁享,死亡現(xiàn)場(chǎng)離奇詭異余素,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)炊昆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門桨吊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人凤巨,你說(shuō)我怎么就攤上這事视乐。” “怎么了敢茁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵佑淀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我彰檬,道長(zhǎng)伸刃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任逢倍,我火速辦了婚禮捧颅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘较雕。我一直安慰自己隘道,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布郎笆。 她就那樣靜靜地躺著谭梗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宛蚓。 梳的紋絲不亂的頭發(fā)上激捏,一...
    開(kāi)封第一講書(shū)人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音凄吏,去河邊找鬼远舅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛痕钢,可吹牛的內(nèi)容都是我干的图柏。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼任连,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蚤吹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤裁着,失蹤者是張志新(化名)和其女友劉穎繁涂,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體二驰,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扔罪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桶雀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矿酵。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矗积,靈堂內(nèi)的尸體忽然破棺而出坏瘩,到底是詐尸還是另有隱情,我是刑警寧澤漠魏,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布倔矾,位于F島的核電站,受9級(jí)特大地震影響柱锹,放射性物質(zhì)發(fā)生泄漏哪自。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一禁熏、第九天 我趴在偏房一處隱蔽的房頂上張望壤巷。 院中可真熱鬧,春花似錦瞧毙、人聲如沸胧华。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)矩动。三九已至,卻和暖如春释漆,著一層夾襖步出監(jiān)牢的瞬間悲没,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工男图, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留示姿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓逊笆,卻偏偏與公主長(zhǎng)得像栈戳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子难裆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容