<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    實戰 Groovy: for each 剖析(轉載)

    迭代是編程的基礎。您經常會遇到需要進行逐項遍歷的內容,比如 List、File 和 JDBC ResultSet。Java 語言幾乎總是提供了某種方法幫助您逐項遍歷所需的內容,但令人沮喪的是,它并沒有給出一種標準方法。Groovy 的迭代方法非常實用,在這一點上,Groovy 編程與 Java 編程截然不同。通過一些代碼示例,本文將介紹 Groovy 的萬能的 each() 方法,從而將 Java 語言的那些迭代怪癖拋在腦后。

    Java 迭代策略

    假設您有一個 Java 編程語言的 java.util.List。清單 1 展示了在 Java 語言中如何使用編程實現迭代:


    清單 1. Java 列表迭代
    import java.util.*;  
    public class ListTest{
     public static void main(String[] args){
     List<String> list = new ArrayList<String>();
     list.add("Java");
     list.add("Groovy");
     list.add("JavaScript");
     for(Iterator<String> i = list.iterator(); i.hasNext();){
     String language = i.next();
     System.out.println("I know " + language);
     }
     }
     }

    由于提供了大部分集合類都可以共享的 java.lang.Iterable 接口,您可以使用相同的方法遍歷 java.util.Setjava.util.Queue。

    關于本系列

    Groovy 是一款運行在 Java 平臺之上的現代編程語言。它能夠與現有 Java 代碼無縫集成,同時引入了閉包和元編程等出色的新特性。簡而言之,Groovy 類似于 21 世紀的 Java 語言。

    如果要將新工具集成到開發工具箱中,最關鍵的是理解什么時候需要使用它以及什么時候不適合使用它。Groovy 可以變得非常強大,但前提是它被適當地應用到合適的場景中。因此, 實戰 Groovy 系列旨在展示 Groovy 的實際使用,以及何時和如何成功應用它。

    現在,假設該語言存儲在 java.util.Map 中。在編譯時,嘗試對 Map 獲取 Iterator 會導致失敗 — Map 并沒有實現 Iterable 接口。幸運的是,可以調用 map.keySet() 返回一個 Set,然后就可以繼續處理。這些小差異可能會影響您的速度,但不會妨礙您的前進。需要注意的是,ListSetQueue 實現了 Iterable,但是 Map 沒有 — 即使它們位于相同的 java.util 包中。

    現在假設該語言存在于 String 數組中。數組是一種數據結構,而不是類。不能對 String 數組調用 .iterator(),因此必須使用稍微不同的迭代策略。您再一次受到阻礙,但可以使用如清單 2 所示的方法解決問題:


    清單 2. Java 數組迭代
    public class ArrayTest{
     public static void main(String[] args){
     String[] list = {"Java", "Groovy", "JavaScript"};
     for(int i = 0; i < list.length; i++){
     String language = list[i];
     System.out.println("I know " + language);
     }
     }
     }

    但是等一下 — 使用 Java 5 引入的 for-each 語法怎么樣?它可以處理任何實現 Iterable 的類和數組,如清單 3 所示:


    清單 3. Java 語言的 for-each 迭代
    import java.util.*;
     public class MixedTest{
     public static void main(String[] args){
     List<String> list = new ArrayList<String>();
     list.add("Java");
     list.add("Groovy");
     list.add("JavaScript");
     for(String language: list){
     System.out.println("I know " + language);
     }
    String[] list2 = {"Java", "Groovy", "JavaScript"};
     for(String language: list2){
     System.out.println("I know " + language);
     }
     }
     }

    因此,您可以使用相同的方法遍歷數組和集合(Map 除外)。但是如果語言存儲在 java.io.File,那該怎么辦?如果存儲在 JDBC ResultSet,或者存儲在 XML 文檔、java.util.StringTokenizer 中呢?面對每一種情況,必須使用一種稍有不同的迭代策略。這樣做并不是有什么特殊目的 — 而是因為不同的 API 是由不同的開發人員在不同的時期開發的 — 但事實是,您必須了解 6 個 Java 迭代策略,特別是使用這些策略的特殊情況。

    Eric S. Raymond 在他的 The Art of Unix Programming 一書中解釋了 “最少意外原則”。他寫道,“要設計可用的接口,最好不要設計全新的接口模型。新鮮的東西總是難以入門;會為用戶帶來學習的負擔,因此應當盡量減少新內 容。”Groovy 對迭代的態度正是采納了 Raymond 的觀點。在 Groovy 中遍歷幾乎任何結構時,您只需要使用 each() 這一種方法。


    Groovy 中的列表迭代

    首先,我將 清單 3 中的 List 重構為 Groovy。在這里,只需要直接對列表調用 each() 方法并傳遞一個閉包,而不是將 List 轉換成 for 循環(順便提一句,這樣做并不是特別具有面向對象的特征,不是嗎)。

    創建一個名為 listTest.groovy 的文件并添加清單 4 中的代碼:


    清單 4. Groovy 列表迭代
    def list = ["Java", "Groovy", "JavaScript"] list.each{language->   println language } 

    清單 4 中的第一行是 Groovy 用于構建 java.util.ArrayList 的便捷語法??梢詫?println list.class 添加到此腳本來驗證這一點。接下來,只需對列表調用 each(),并在閉包體內輸出 language 變量。在閉包的開始處使用 language-> 語句命名 language 變量。如果沒有提供變量名,Groovy 提供了一個默認名稱 it。在命令行提示符中輸入 groovy listTest 運行 listTest.groovy。

    清單 5 是經過簡化的 清單 4 代碼版本:


    清單 5. 使用 Groovy 的 it 變量的迭代
     // shorter, using the default it variable 
    def list = ["Java", "Groovy", "JavaScript"]
     list.each{ println it }
     // shorter still, using an anonymous
    list ["Java", "Groovy", "JavaScript"].each{ println it }

    Groovy 允許您對數組和 List 交替使用 each() 方法。為了將 ArrayList 改為 String 數組,必須將 as String[] 添加到行末,如清單 6 所示:


    清單 6. Groovy 數組迭代
    def list = ["Java", "Groovy", "JavaScript"] as String[] list.each{println it} 

    在 Groovy 中普遍使用 each() 方法,并且 getter 語法非常便捷(getClass()class 是相同的調用),這使您能夠編寫既簡潔又富有表達性的代碼。例如,假設您希望利用反射顯示給定類的所有公共方法。清單 7 展示了這個例子:


    清單 7. Groovy 反射
    def s = "Hello World"
     println s
    println s.class
     s.class.methods.each{println it}
     //output:
    $ groovy reflectionTest.groovy
     Hello World
     class java.lang.String
     public int java.lang.String.hashCode()
     public volatile int java.lang.String.compareTo(java.lang.Object)
     public int java.lang.String.compareTo(java.lang.String)
     public boolean java.lang.String.equals(java.lang.Object) ...

    腳本的最后一行調用 getClass() 方法。java.lang.Class 提供了一個 getMethods() 方法,后者返回一個數組。通過將這些操作串連起來并對 Method 的結果數組調用 each(),您只使用了一行代碼就完成了大量工作。

    但是,與 Java for-each 語句不同的是,萬能的 each() 方法并不僅限于 List 和數組。在 Java 語言中,故事到此結束。然而,在 Groovy 中,故事才剛剛開始。

    Map 迭代

    從前文可以看到,在 Java 語言中,無法直接迭代 Map。在 Groovy 中,這完全不是問題,如清單 8 所示:


    清單 8. Groovy map 迭代
    def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"] map.each{ println it } 

    要處理名稱/值對,可以使用隱式的 getKey()getValue() 方法,或在包的開頭部分顯式地命名變量,如清單 9 所示:


    清單 9. 從 map 獲得鍵和值
    def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"]
     map.each{
     println it.key
     println it.value
     }
    map.each{k,v->
     println k
     println v
     }

    可以看到,迭代 Map 和迭代其它任何集合一樣自然。

    在繼續研究下一個迭代例子前,應當了解 Groovy 中有關 Map 的另一個語法。與在 Java 語言中調用 map.get("Java") 不一樣,可以簡化對 map.Java 的調用,如清單 10 所示:


    清單 10. 獲得 map 值
    def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"]
     //identical results
     println map.get("Java")
     println map.Java

    不可否認,Groovy 針對 Map 的這種便捷語法非常酷,但這也是在對 Map 使用反射時引起一些常見問題的原因。對 list.class 的調用將生成 java.util.ArrayList,而調用 map.class 返回 null。這是因為獲得 map 元素的便捷方法覆蓋了實際的 getter 調用。Map 中的元素都不具有 class 鍵,因此調用實際會返回 null,如清單 11 的示例所示:


    清單 11. Groovy map 和 null
    def list = ["Java", "Groovy", "JavaScript"]
     println list.class
     // java.util.ArrayList
     def map = ["Java":"server", "Groovy":"server", "JavaScript":"web"]
     println map.class
     // null
     map.class = "I am a map element"
     println map.class
     // I am a map element
     println map.getClass()
     // class
     java.util.LinkedHashMap

    這是 Groovy 比較罕見的打破 “最少意外原則” 的情況,但是由于從 map 獲取元素要比使用反射更加常見,因此我可以接受這一例外。


    String 迭代

    現在您已經熟悉 each() 方法了,它可以出現在所有相關的位置。假設您希望迭代一個 String,并且是逐一迭代字符,那么馬上可以使用 each() 方法。如清單 12 所示:


    清單 12. String 迭代
    def name = "Jane Smith" name.each{letter->
     println letter
     }

    這提供了所有的可能性,比如使用下劃線替代所有空格,如清單 13 所示:


    清單 13. 使用下劃線替代空格

    def name = "Jane Smith"
     println "replace spaces"
    name.each{
     if(it == " "){
     print "_"
     }else{
     print it
     }
     }
     // output Jane_Smith

    當然,在替換一個單個字母時,Groovy 提供了一個更加簡潔的替換方法。您可以將清單 13 中的所有代碼合并為一行代碼:"Jane Smith".replace(" ", "_")。但是對于更復雜的 String 操作,each() 方法是最佳選擇。


    Range 迭代

    Groovy 提供了原生的 Range 類型,可以直接迭代。使用兩個點分隔的所有內容(比如 1..10)都是一個 Range。清單 14 展示了這個例子:


    清單 14. Range 迭代
    def range = 5..10 range.each{
     println it
     }
     //output: 5 6 7 8 9 10

    Range 不局限于簡單的 Integer??紤]清單 15 在的代碼,其中迭代 DateRange


    清單 15. Date 迭代
    def today = new Date()
     def nextWeek = today + 7
     (today..nextWeek).each{
     println it
     }
    //output: Thu Mar 12 04:49:35 MDT 2009
     Fri Mar 13 04:49:35 MDT 2009
    Sat Mar 14 04:49:35 MDT 2009
    Sun Mar 15 04:49:35 MDT 2009
    Mon Mar 16 04:49:35 MDT 2009
    Tue Mar 17 04:49:35 MDT 2009
    Wed Mar 18 04:49:35 MDT 2009
    Thu Mar 19 04:49:35 MDT 2009

    可以看到,each() 準確地出現在您所期望的位置。Java 語言缺乏原生的 Range 類型,但是提供了一個類似地概念,采取 enum 的形式。毫不奇怪,在這里 each() 仍然派得上用場。


    Enumeration 類型

    Java enum 是按照特定順序保存的隨意的值集合。清單 16 展示了 each() 方法如何自然地配合 enum,就好象它在處理 Range 操作符一樣:


    清單 16. enum 迭代
    enum DAY{
     MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
     FRIDAY, SATURDAY, SUNDAY
     }
     DAY.each{
     println it
     }
     (DAY.MONDAY..DAY.FRIDAY).each{
     println it
     }

    在 Groovy 中,有些情況下,each() 這個名稱遠未能表達它的強大功能。在下面的例子中,將看到使用特定于所用上下文的方法對 each() 方法進行修飾。Groovy eachRow() 方法就是一個很好的例子。


    SQL 迭代

    在處理關系數據庫表時,經常會說 “我需要針對表中的每一行執行操作”。比較一下前面的例子。您很可能會說 “我需要對列表中的每一種語言執行一些操作”。根據這個道理,groovy.sql.Sql 對象提供了一個 eachRow() 方法,如清單 17 所示:


    清單 17. ResultSet 迭代
    import groovy.sql.*
     def sql = Sql.newInstance(
     "jdbc:derby://localhost:1527/MyDbTest;create=true",
     "username",
     "password",
     "org.apache.derby.jdbc.ClientDriver")
     println("grab a specific field")
     sql.eachRow("select name from languages"){ row ->
     println row.name
     }
     println("grab all fields")
     sql.eachRow("select * from languages"){ row ->
     println("Name: ${row.name}")
     println("Version: ${row.version}")
     println("URL: ${row.url}\n")
     }

    該腳本的第一行代碼實例化了一個新的 Sql 對象:設置 JDBC 連接字符串、用戶名、密碼和 JDBC 驅動器類。這時,可以調用 eachRow() 方法,傳遞 SQL select 語句作為一個方法參數。在閉包內部,可以引用列名(nameversion、url),就好像實際存在 getName()、getVersion()getUrl() 方法一樣。

    這顯然要比 Java 語言中的等效方法更加清晰。在 Java 中,必須創建單獨的 DriverManager、ConnectionStatementJDBCResultSet,然后必須在嵌套的 try/catch/finally 塊中將它們全部清除。

    對于 Sql 對象,您會認為 each()eachRow() 都是一個合理的方法名。但是在接下來的示例中,我想您會認為 each() 這個名稱并不能充分表達它的功能。


    文件迭代

    我從未想過使用原始的 Java 代碼逐行遍歷 java.io.File。當我完成了所有的嵌套的 BufferedReaderFileReader 后(更別提每個流程末尾的所有異常處理),我已經忘記最初的目的是什么。

    清單 18 展示了使用 Java 語言完成的整個過程:


    清單 18. Java 文件迭代
    import java.io.BufferedReader;
     import java.io.FileNotFoundException;
     import java.io.FileReader; import java.io.IOException;
     public class WalkFile {
     public static void main(String[] args) {
     BufferedReader br = null;
     try {
     br = new BufferedReader(new FileReader("languages.txt"));
     String line = null;
     while((line = br.readLine()) != null) {
     System.out.println("I know " + line);
     }
     }
     catch(FileNotFoundException e) {
     e.printStackTrace();
     }
     catch(IOException e) {
     e.printStackTrace();
     }
     finally {
     if(br != null) {
     try {
     br.close();
     }
    catch(IOException e) {
     e.printStackTrace();
     }
     }
     }
     }
     }

    清單 19 展示了 Groovy 中的等效過程:


    清單 19. Groovy 文件迭代
    def f = new File("languages.txt") f.eachLine{language->
     println "I know ${language}"
    }

    這正是 Groovy 的簡潔性真正擅長的方面?,F在,我希望您了解為什么我將 Groovy 稱為 “Java 程序員的 DSL”。

    注意,我在 Groovy 和 Java 語言中同時處理同一個 java.io.File 類。如果該文件不存在,那么 Groovy 代碼將拋出和 Java 代碼相同的 FileNotFoundException 異常。區別在于,Groovy 沒有已檢測的異常。在 try/catch/finally 塊中封裝 eachLine() 結構是我自己的愛好 — 而不是一項語言需求。對于一個簡單的命令行腳本中,我欣賞 清單 19 中的代碼的簡潔性。如果我在運行應用服務的同時執行相同的迭代,我不能對這些異常坐視不管。我將在與 Java 版本相同的 try/catch 塊中封裝 eachLine() 塊。

    File 類對 each() 方法進行了一些修改。其中之一就是 splitEachLine(String separator, Closure closure)。這意味著您不僅可以逐行遍歷文件,同時還可以將它分為不同的標記。清單 20 展示了一個例子:


    清單 20. 分解文件的每一行
    // languages.txt
     // notice the space between the language and the version Java 1.5 Groovy 1.6 JavaScript 1.x
     // splitTest.groovy
    def f = new File("languages.txt")
     f.splitEachLine(" "){words->
     words.each{ println it
     }
     }
    // output Java 1.5 Groovy 1.6 JavaScript 1.x

    如果處理的是二進制文件,Groovy 還提供了一個 eachByte() 方法。

    當然,Java 語言中的 File 并不總是一個文件 — 有時是一個目錄。Groovy 還提供了一些 each() 修改以處理子目錄。


    目錄迭代

    使用 Groovy 代替 shell 腳本(或批處理腳本)非常容易,因為您能夠方便地訪問文件系統。要獲得當前目錄的目錄列表,參見清單 21:


    清單 21. 目錄迭代
    def dir = new File(".") dir.eachFile{file->
     println file
     }

    eachFile() 方法同時返回了文件和子目錄。使用 Java 語言的 isFile()isDirectory() 方法,可以完成更復雜的事情。清單 22 展示了一個例子:


    清單 22. 分離文件和目錄
    def dir = new File(".") dir.eachFile{file->
     if(file.isFile()){
     println "FILE: ${file}"
     }else if(file.isDirectory()){
     println "DIR: ${file}"
     }else{
     println "Uh, I'm not sure what it is..."
     }
     }

    由于兩種 Java 方法都返回 boolean 值,可以在代碼中添加一個 Java 三元操作符。清單 23 展示了一個例子:


    清單 23. 三元操作符
    def dir = new File(".")
     dir.eachFile{file->
     println file.isDirectory() ? "DIR: ${file}" : "FILE: ${file}"
     }

    如果只對目錄有興趣,那么可以使用 eachDir() 而不是 eachFile()。還提供了 eachDirMatch()eachDirRecurse() 方法。

    可以看到,對 File 僅使用 each() 方法并不能提供足夠的含義。典型 each() 方法的語義保存在 File 中,但是方法名更具有描述性,從而提供更多有關這個高級功能的信息。


    URL 迭代

    理解了如何遍歷 File 后,可以使用相同的原則遍歷 HTTP 請求的響應。Groovy 為 java.net.URL 提供了一個方便的(和熟悉的)eachLine() 方法。

    例如,清單 24 將逐行遍歷 ibm.com 主頁的 HTML:


    清單 24. URL 迭代
    def url = new URL("http://www.ibm.com")
     url.eachLine{line->
     println line
     }

    當然,如果這就是您的目的的話,Groovy 提供了一個只包含一行代碼的解決辦法,這主要歸功于 toURL() 方法,它被添加到所有 Strings"http://www.ibm.com".toURL().eachLine{ println it }。

    但是,如果希望對 HTTP 響應執行一些更有用的操作,該怎么辦呢?具體來講,如果發出的請求指向一個 RESTful Web 服務,而該服務包含您要解析的 XML,該怎么做呢?each() 方法將在這種情況下提供幫助。


    XML 迭代

    您已經了解了如何對文件和 URL 使用 eachLine() 方法。XML 給出了一個稍微有些不同的問題 — 與逐行遍歷 XML 文檔相比,您可能更希望對逐個元素進行遍歷。

    例如,假設您的語言列表存儲在名為 languages.xml 的文件中,如清單 25 所示:


    清單 25. languages.xml 文件
    <langs>
     <language>Java</language>
     <language>Groovy</language>
     <language>JavaScript</language>
     </langs>

    Groovy 提供了一個 each() 方法,但是需要做一些修改。如果使用名為 XmlSlurper 的原生 Groovy 類解析 XML,那么可以使用 each() 遍歷元素。參見清單 26 所示的例子:


    清單 26. XML 迭代
    def langs = new XmlSlurper().parse("languages.xml")
     langs.language.each{
     println it
     }
     //output Java Groovy JavaScript

    langs.language.each 語句從名為 <language><langs> 提取所有元素。如果同時擁有 <format><server> 元素,它們將不會出現在 each() 方法的輸出中。

    如果覺得這還不夠的話,那么假設這個 XML 是通過一個 RESTful Web 服務的形式獲得,而不是文件系統中的文件。使用一個 URL 替換文件的路徑,其余代碼仍然保持不變,如清單 27 所示:


    清單 27. Web 服務調用的 XML 迭代
    def langs = new XmlSlurper().parse("http://somewhere.com/languages")
     langs.language.each{
     println it
     }

    這真是個好方法,each() 方法在這里用得很好,不是嗎?


    結束語

    在使用 each() 方法的整個過程中,最妙的部分在于它只需要很少的工作就可以處理大量 Groovy 內容。解了 each() 方法之后,Groovy 中的迭代就易如反掌了。正如 Raymond 所說,這正是關鍵所在。一旦了解了如何遍歷 List,那么很快就會掌握如何遍歷數組、Map、String、Rangeenum、SQL ResultSet、File、目錄和 URL,甚至是 XML 文檔的元素。

    本文的最后一個示例簡單提到使用 XmlSlurper 實現 XML 解析。在下一期文章中,我將繼續討論這個問題,并展示使用 Groovy 進行 XML 解析有多么簡單!您將看到 XmlParserXmlSlurper 的實際使用,并更好地了解 Groovy 為什么提供兩個類似但又略有不同的類實現 XML 解析。到那時,希望您能發現 Groovy 的更多實際應用。

    posted on 2011-08-04 11:50 小羅 閱讀(546) 評論(0)  編輯  收藏 所屬分類: groovy


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    <2011年8月>
    31123456
    78910111213
    14151617181920
    21222324252627
    28293031123
    45678910

    導航

    統計

    常用鏈接

    留言簿

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    相冊

    收藏夾

    Web Framework

    常上的技術網站

    查找資料的java網站

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: a级毛片免费网站| 免费人妻精品一区二区三区| 亚洲精品动漫免费二区| 国产精品免费视频观看拍拍| 亚洲无圣光一区二区| 亚洲国产精品国产自在在线| 无人在线观看免费高清视频| 四虎成人精品国产永久免费无码| 亚洲理论片在线观看| 亚洲午夜福利AV一区二区无码| 永久在线毛片免费观看| 99久久99这里只有免费费精品| a毛片在线看片免费| 成人免费视频一区二区| 老司机福利在线免费观看| 亚洲AV一区二区三区四区| 亚洲国产成人精品青青草原| 亚洲男人天堂av| 久久精品国产亚洲av麻豆| 亚洲午夜久久久久久久久久| 久久久青草青青国产亚洲免观 | 亚洲gv猛男gv无码男同短文| 亚洲热线99精品视频| 久久乐国产精品亚洲综合| 亚洲高清国产拍精品青青草原| 免费人成网站7777视频| 高清在线亚洲精品国产二区| 亚洲精品高清在线| 亚洲a一级免费视频| 亚洲免费在线视频观看| 亚洲日韩精品无码专区加勒比| 国产精品国产亚洲区艳妇糸列短篇 | 亚洲av无码成h人动漫无遮挡| 亚洲精品福利网站| 久久综合亚洲色hezyo| 成在人线av无码免费高潮水| 国产中文字幕在线免费观看| 久久黄色免费网站| 成人av免费电影| 国精无码欧精品亚洲一区| 亚洲国产高清美女在线观看|