国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

淺析Java 8中的集合遍歷

2014-10-22 12:22錢宇虹
軟件工程 2014年10期
關(guān)鍵詞:集合

摘 要:Java平臺提供了多種方式遍歷對象的集合,其中包括今年3月19日發(fā)布的Java 8中引入的新特性。本文回顧了迭代器,著重分析了主動式迭代器和被動式迭代器之間的差異,研究了Java 8的foreach()方法和Stream API如何改進(jìn)和并行化Java迭代器的行為,然后對主動迭代、流和并行流這三種方法進(jìn)行了性能比較??傊?,Java 8的迭代器可讀性更好,不易出錯(cuò),也更容易并行化。

關(guān)鍵詞:Java8;集合;迭代器

中圖分類號:TP311 文獻(xiàn)標(biāo)識碼:A

1 引言(Introduction)

在編程世界里一般需要提供一種機(jī)制遍歷軟件對象的集合。大多數(shù)編程語言都有類似于數(shù)組的功能并且直接支持?jǐn)?shù)組元素的遍歷,但是現(xiàn)代的編程語言還支持更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如列表、集合、映射和樹,遍歷能力是通過公共方法提供,而內(nèi)部細(xì)節(jié)都隱藏在類的私有部分,所以程序員不需要了解其內(nèi)部實(shí)現(xiàn)就能夠遍歷這些數(shù)據(jù)結(jié)構(gòu)中的元素,這就是迭代的目的。

迭代器是對集合中的所有元素進(jìn)行順序訪問并可以對每個(gè)元素執(zhí)行某些操作的機(jī)制。迭代器在本質(zhì)上提供了在封裝的對象集合上做“循環(huán)”的裝置。常見的使用迭代器的例子有:

訪問目錄中的每個(gè)文件并顯示文件名;訪問隊(duì)列中的每個(gè)客戶(如銀行排隊(duì))并判斷他或她等待了多久。使用迭代器時(shí),一般情況下可以循環(huán)嵌套,即可以在同一時(shí)間做多個(gè)遍歷;迭代器應(yīng)該是無損的,即迭代行為不應(yīng)該改變集合本身,如迭代時(shí)不要從集合中移除或插入元素;在某些情況下你還需要使用迭代器的不同的遍歷方法,例如,樹的前序遍歷和后序遍歷,或者深度優(yōu)先和廣度優(yōu)先遍歷。

根據(jù)Gang of Four,迭代器設(shè)計(jì)模式是一種行為模式,其核心思想是負(fù)責(zé)訪問和遍歷列表中的對象,并把這些對象放到一個(gè)迭代器對象中[1]。迭代器的實(shí)現(xiàn)方法根據(jù)誰來控制迭代分為兩種:主動迭代和被動迭代。主動迭代器是由客戶程序創(chuàng)建迭代器,調(diào)用next()行進(jìn)到下一個(gè)元素,測試查看是否所有元素已被訪問等等,總之客戶程序是可以操作的。這種方法在象C++這樣的語言中最為常見,在GoF的書中也是最為關(guān)注的方法,主動迭代器在Java 8之前可以說是Java的唯一選擇。被動迭代器則是迭代器本身控制迭代,即迭代器自行next()向下走,針對客戶程序來說迭代是透明的,是不能操作的。這種方法在象LISP這樣的語言中很常見。隨著Java 8的發(fā)布,這種迭代方法也成為Java程序員的一個(gè)選擇。

2 Java 8之前的迭代(Iteration before Java 8)

為了說明Java中的各種迭代方法,我們需要一個(gè)集合并對集合中的元素做些操作,本文選擇代表事物或人物名稱的字符串的集合,我們將簡單地打印集合中的每個(gè)名稱到標(biāo)準(zhǔn)輸出。這些基本的思想很容易擴(kuò)展到更為復(fù)雜的對象的集合(如員工),并在處理每一個(gè)對象的時(shí)候可以涉及更為復(fù)雜的操作。

在Java 1.0和1.1中兩個(gè)主要的集合類是Vector(向量)和Hashtable(哈希表),迭代器是通過一個(gè)叫做Enumeration(枚舉)的類實(shí)現(xiàn)的。今天無論是Vector還是Hashtable都是泛型類,但退回到那時(shí)泛型還不是Java語言的一部分。代碼清單1演示了使用枚舉來處理字符串向量的方法。

Vector names = new Vector();

names.add("Apple");

names.add("Orange");

Enumeration e = names.elements();

while (e.hasMoreElements())

{

String name = (String) e.nextElement();

System.out.println(name);

}

代碼清單1:使用枚舉處理字符串向量

List names = new LinkedList();

names.add("Apple");

names.add("Orange");

Iterator i = names.iterator();

while (i.hasNext())

{

String name = (String) i.next();

System.out.println(name);

}

代碼清單2:使用迭代器處理字符串列表

Java 1.2推出了集合類(Collections),并通過一個(gè)迭代器類(Iterator)實(shí)現(xiàn)了迭代器設(shè)計(jì)模式。因?yàn)楫?dāng)時(shí)在Java 1.2中還沒有泛型,所以對迭代器返回的對象進(jìn)行強(qiáng)制類型轉(zhuǎn)換仍然是必要的。對于Java版本1.2至1.4,遍歷字符串列表如代碼清單2所示。

Java 5給出了泛型、Iterable接口和增強(qiáng)for循環(huán)。在增強(qiáng)for循環(huán)中,迭代器的創(chuàng)建和調(diào)用它的hasNext()和next()方法都發(fā)生在幕后,不需要明確地寫在代碼中,因此代碼顯得更為緊湊。使用Java 5,我們的例子類似代碼清單3所示,請注意,在Java 5我們使用的仍然是主動迭代器。

List names = new

LinkedList();

names.add("Apple");

names.add("Orange");

for (String name : names)

System.out.println(name);

代碼清單3:使用泛型和增強(qiáng)for循環(huán)

List names = new LinkedList<>();

names.add("Apple");

names.add("Orange");

names.forEach(name-> System.out.println(name));

代碼清單4:Java8使用forEach()方法進(jìn)行迭代

Java 7為了避免泛型的冗長給出了鉆石運(yùn)算符<>,從而避免了使用new運(yùn)算符實(shí)例化泛型類時(shí)重復(fù)指定數(shù)據(jù)類型。從Java 7開始,代碼清單3中的第一行可以簡化成以下形式:List names = new LinkedList<>();

3 Java 8中的迭代(Iteration in Java 8)

3.1 forEach()方法

Java8為我們提供了新的迭代途徑,它使用lambda表達(dá)式開展集合的遍歷。Java8最主要的新特性就是lambda表達(dá)式以及與此相關(guān)的特性,如流(streams),方法引用(method references)和功能接口(functional interfaces)。正是因?yàn)檫@些新特性使我們能夠使用被動迭代器而不是傳統(tǒng)的主動迭代器,特別是Iterable接口提供了一個(gè)被動迭代器的缺省方法(default method)叫做forEach()。缺省方法是Java 8的又一個(gè)新特性,是一個(gè)接口方法的缺省實(shí)現(xiàn),在這種情況下,forEach()方法實(shí)際上是用類似于代碼清單3的主動迭代器方式來實(shí)現(xiàn)的。

實(shí)現(xiàn)了Iterable接口的集合類(如:所有列表List、集合set)現(xiàn)在都有一個(gè)forEach()方法,這個(gè)方法接收一個(gè)功能接口參數(shù),實(shí)際上傳遞給forEach()方法的參數(shù)是一個(gè)lambda表達(dá)式。使用Java 8的功能,我們的例子將演變成代碼清單4中所示的形式。

請注意清單4中的被動迭代與前面三個(gè)清單中的主動迭代之間的差異。在主動迭代中由循環(huán)結(jié)構(gòu)控制迭代,并且每次通過循環(huán)從列表中獲取一個(gè)對象,然后打印出來。而在清單4中沒有顯式的循環(huán)結(jié)構(gòu),我們只是告訴forEach()方法對列表中的對象實(shí)施打印,迭代控制隱含在forEach()方法中。

3.2 流

如果要做一些比打印名字稍微復(fù)雜一點(diǎn)的事情,比如,計(jì)算以字母A開頭的人名的個(gè)數(shù),就需要用lambda表達(dá)式,或者使用Java 8的流API(Stream API)來實(shí)現(xiàn)更復(fù)雜的邏輯了。

流是應(yīng)用在一組元素上的一次執(zhí)行的操作序列。集合和數(shù)組都可以用來產(chǎn)生流,因此它們稱作數(shù)據(jù)源,但流不存儲集合中的元素,相反,流是通過管道(pipeline)操作來自數(shù)據(jù)源的值序列的一種機(jī)制。流管道(Stream Pipeline)由數(shù)據(jù)源(Stream Source)、若干中間操作(Intermediate Operations)和一個(gè)最終操作(Terminal Operation)組成,中間操作對數(shù)據(jù)集完成過濾、檢索等中間業(yè)務(wù),而最終操作完成對數(shù)據(jù)集處理的最終結(jié)果,或者調(diào)用forEach()方法。

List names = new LinkedList<>();

names.add("Annie");

names.add("Alice");

names.add("Bob");

long count = names.stream()

.filter(name -> name.startsWith("A")

.count();

代碼清單5:Java8使用流管道計(jì)算以字母A開頭的人名的

個(gè)數(shù)

List names = new LinkedList<>();

names.add("Annie");

names.add("Alice");

names.add("Bob");

long count = 0;

for (String name : names){

if (name.startsWith("A"))

++count;

}

代碼清單6:Java 7使用主動迭代計(jì)算以字母A開頭的人

名的個(gè)數(shù)

如清單5所示,列表names用于創(chuàng)建流,然后使用過濾器對數(shù)據(jù)集進(jìn)行過濾,filter()方法只過濾出以字母A開頭的名字,該方法的參數(shù)是一個(gè)lambda表達(dá)式[2]。最后,流的count()方法作為最終操作,得到應(yīng)用結(jié)果。

中間操作除了filter()之外,還有distinct()、sorted()、map()等等,其一般是對數(shù)據(jù)集的整理(過濾、排序、匹配、抽取等等),返回值一般也是數(shù)據(jù)集。

最終方法往往是完成對數(shù)據(jù)集中數(shù)據(jù)的處理,如forEach(),還有allMatch()、anyMatch()、findAny()、findFirst(),數(shù)值計(jì)算類的方法有sum()、max()、min()、averag()等等。最終方法也可以是對集合的處理,如reduce()、collect()等等。reduce()方法的處理方式一般是每次都產(chǎn)生新的數(shù)據(jù)集,而collect()方法是在原數(shù)據(jù)集的基礎(chǔ)上進(jìn)行更新,過程中不產(chǎn)生新的數(shù)據(jù)集。流管道操作更為詳細(xì)的資料請參閱Java Tutorial[3]。

為了理解Java8的流的重要性,請比較代碼清單5和代碼清單6。代碼清單6是大多數(shù)Java開發(fā)人員比較熟悉的,它使用主動式迭代完成同樣的功能。很顯然,只要掌握了流的基本概念和操作,清單5中的方法易讀性更好且不易出錯(cuò),特別是,在多線程環(huán)境下清單6中的邏輯不是線程安全的,而清單5是線程安全的,它也更容易進(jìn)行并行化。

Java8集合類不僅具有Stream()方法,該方法返回一個(gè)連續(xù)的數(shù)據(jù)流,Java8集合類還有一個(gè)parallelStream()方法,該方法返回一個(gè)并行流。并行流的作用在于允許管道操作同時(shí)在不同的Java線程中執(zhí)行以提高性能;但要注意,集合元素的處理順序可能發(fā)生改變。測試顯示,使用并行流打印列表中的名字,他們打印的順序不同于他們在列表中出現(xiàn)的順序。下面是使用并行流之后的代碼:

long count=names.parallelStream().filter(name-> name.startsWith("A")).count();

4 性能比較(Performance comparison)

雖然使用Java 8中的被動迭代器有多種優(yōu)勢,那么它是否提供更快的集合處理速度呢?為了比較主動迭代器、流和并行流的性能,下面我們還是以計(jì)算字母A開頭的人名的個(gè)數(shù)為例,采取上述三種迭代方法做一個(gè)比較測試。在一臺雙核奔騰處理器、4G內(nèi)存、64位Windows 7和64位版本的Java 8配置環(huán)境下,使用五種不同的集合類(LinkedList,ArrayList、HashSet、LinkedHashSet和TreeSet)運(yùn)行上述程序,將平均時(shí)間總結(jié)在如下所示的表1中。

5 結(jié)論(Conclusion)

Java 8支持一種新功能進(jìn)行迭代,它是聲明性的方法,這種新方法帶來的好處是代碼的可讀性更好,不易出錯(cuò),也更容易并行化。然而測試表明,并行流對每一種集合類而言不一定性能更快。

參考文獻(xiàn)(References)

[1] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,Design Patterns: Elements of Reusable Object-OrientedSoftware [A], Addison-Wesley Professional, 1994.

[2] Oracle, The JavaTM Tutorials,Lambda Expressions [EB/OL],http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html.

[3] Oracle, The JavaTM Tutorials,Lesson: Aggregate Operations[EB/OL],http://docs.oracle.com/javase/tutorial/collections/streams/index.html.

作者簡介:

錢宇虹(1967-),女,碩士,副教授.研究領(lǐng)域:軟件開發(fā)與應(yīng)用,軟件工程,軟件測試技術(shù).endprint

Java8集合類不僅具有Stream()方法,該方法返回一個(gè)連續(xù)的數(shù)據(jù)流,Java8集合類還有一個(gè)parallelStream()方法,該方法返回一個(gè)并行流。并行流的作用在于允許管道操作同時(shí)在不同的Java線程中執(zhí)行以提高性能;但要注意,集合元素的處理順序可能發(fā)生改變。測試顯示,使用并行流打印列表中的名字,他們打印的順序不同于他們在列表中出現(xiàn)的順序。下面是使用并行流之后的代碼:

long count=names.parallelStream().filter(name-> name.startsWith("A")).count();

4 性能比較(Performance comparison)

雖然使用Java 8中的被動迭代器有多種優(yōu)勢,那么它是否提供更快的集合處理速度呢?為了比較主動迭代器、流和并行流的性能,下面我們還是以計(jì)算字母A開頭的人名的個(gè)數(shù)為例,采取上述三種迭代方法做一個(gè)比較測試。在一臺雙核奔騰處理器、4G內(nèi)存、64位Windows 7和64位版本的Java 8配置環(huán)境下,使用五種不同的集合類(LinkedList,ArrayList、HashSet、LinkedHashSet和TreeSet)運(yùn)行上述程序,將平均時(shí)間總結(jié)在如下所示的表1中。

5 結(jié)論(Conclusion)

Java 8支持一種新功能進(jìn)行迭代,它是聲明性的方法,這種新方法帶來的好處是代碼的可讀性更好,不易出錯(cuò),也更容易并行化。然而測試表明,并行流對每一種集合類而言不一定性能更快。

參考文獻(xiàn)(References)

[1] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,Design Patterns: Elements of Reusable Object-OrientedSoftware [A], Addison-Wesley Professional, 1994.

[2] Oracle, The JavaTM Tutorials,Lambda Expressions [EB/OL],http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html.

[3] Oracle, The JavaTM Tutorials,Lesson: Aggregate Operations[EB/OL],http://docs.oracle.com/javase/tutorial/collections/streams/index.html.

作者簡介:

錢宇虹(1967-),女,碩士,副教授.研究領(lǐng)域:軟件開發(fā)與應(yīng)用,軟件工程,軟件測試技術(shù).endprint

Java8集合類不僅具有Stream()方法,該方法返回一個(gè)連續(xù)的數(shù)據(jù)流,Java8集合類還有一個(gè)parallelStream()方法,該方法返回一個(gè)并行流。并行流的作用在于允許管道操作同時(shí)在不同的Java線程中執(zhí)行以提高性能;但要注意,集合元素的處理順序可能發(fā)生改變。測試顯示,使用并行流打印列表中的名字,他們打印的順序不同于他們在列表中出現(xiàn)的順序。下面是使用并行流之后的代碼:

long count=names.parallelStream().filter(name-> name.startsWith("A")).count();

4 性能比較(Performance comparison)

雖然使用Java 8中的被動迭代器有多種優(yōu)勢,那么它是否提供更快的集合處理速度呢?為了比較主動迭代器、流和并行流的性能,下面我們還是以計(jì)算字母A開頭的人名的個(gè)數(shù)為例,采取上述三種迭代方法做一個(gè)比較測試。在一臺雙核奔騰處理器、4G內(nèi)存、64位Windows 7和64位版本的Java 8配置環(huán)境下,使用五種不同的集合類(LinkedList,ArrayList、HashSet、LinkedHashSet和TreeSet)運(yùn)行上述程序,將平均時(shí)間總結(jié)在如下所示的表1中。

5 結(jié)論(Conclusion)

Java 8支持一種新功能進(jìn)行迭代,它是聲明性的方法,這種新方法帶來的好處是代碼的可讀性更好,不易出錯(cuò),也更容易并行化。然而測試表明,并行流對每一種集合類而言不一定性能更快。

參考文獻(xiàn)(References)

[1] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,Design Patterns: Elements of Reusable Object-OrientedSoftware [A], Addison-Wesley Professional, 1994.

[2] Oracle, The JavaTM Tutorials,Lambda Expressions [EB/OL],http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html.

[3] Oracle, The JavaTM Tutorials,Lesson: Aggregate Operations[EB/OL],http://docs.oracle.com/javase/tutorial/collections/streams/index.html.

作者簡介:

錢宇虹(1967-),女,碩士,副教授.研究領(lǐng)域:軟件開發(fā)與應(yīng)用,軟件工程,軟件測試技術(shù).endprint

猜你喜歡
集合
化抽象為具體,優(yōu)化“集合”教學(xué)
強(qiáng)大的Collection集合框架
中學(xué)生物學(xué)(2017年3期)2017-04-11
論“子集、全集、補(bǔ)集”
與學(xué)生的一次雙贏探究
論五聲性集合4—23對作品的多層次控制形態(tài)
論述高中數(shù)學(xué)中集合的類型及基本運(yùn)算
一道數(shù)學(xué)填空題引發(fā)對細(xì)節(jié)的思考
解讀《集合》
三年級數(shù)學(xué)《集合》教學(xué)設(shè)計(jì)
洛宁县| 乡宁县| 双鸭山市| 防城港市| 丹棱县| 莱芜市| 临汾市| 美姑县| 南皮县| 潍坊市| 丰宁| 漳浦县| 灵宝市| 舞阳县| 松滋市| 邹城市| 嘉峪关市| 和龙市| 安国市| 健康| 绥阳县| 库车县| 华宁县| 广州市| 武乡县| 炎陵县| 台东市| 揭阳市| 正镶白旗| 新源县| 宁明县| 唐海县| 泌阳县| 高碑店市| 陆川县| 高雄县| 阜新市| 张家界市| 长宁县| 镇平县| 全南县|