2013-01-15 66 views
13

我有以下示例代碼,其中包含3嵌套for循環。番石榴迭代器,並遍歷列表對象內的列表

for(Continent continent : continentList) 
{ 
    for(Country country : continent.getCountries()) 
    { 
     for(City city : country.getCities()) 
     { 
      //Do stuff with city objects 
     } 
    } 
} 

有沒有什麼辦法來模仿這個嵌套的for-loop使用Guava和迭代器?我一直在努力尋找一個沒有太多運氣的合適例子,我想知道有人能幫助我嗎?我的一位同事提到使用過濾器。

編輯:修正了示例代碼中的小錯誤

+3

你可以嵌套你的映射。恕我直言,它可能是更簡單的嵌套循環,至少外層循環。 –

+3

在第3行中,不應該是「continent.getCountries()」嗎? – Chris

+0

您可以使用番石榴「變換」和「concat」來創建三元組的單個列表,然後對其進行迭代,但至少對於Java 7來說,代碼將會非常難看。我會留下嵌套的循環。 – Chris

回答

11

正如彼得Lawrey評論的告誡,這是幾乎可以肯定將是作爲嵌套循環簡單。更結束了,Guava documentation給出了這樣的警告:

勢在必行代碼應該是你默認情況下,您的第一選擇如Java 7.除非你有絕對的把握下列之一的,則不應使用功能成語:

  • 使用功能性成語會爲您的整個項目節省代碼行 的淨節省。將函數的定義 移動到另一個文件或常量中並沒有幫助。
  • 爲了提高效率,您需要對轉換後的 集合進行延遲計算,並且無法解決顯式計算的集合問題。 此外,您已閱讀並重讀有效的Java,項目55和 除了遵循這些說明之外,您實際上已經完成 基準測試以證明此版本更快,並且可以引用 數字來證明它。

請務必使用番石榴的功能 實用程序時,該做事的傳統方式勢在必行不 更具可讀性。嘗試寫出來。那麼糟糕?難道那個 比可怕的功能性方法更具可讀性嗎?您是 即將嘗試?

不過,如果你對無視勸告堅持,你可以使用類似這樣的怪物(注意:我還沒有真正試圖編譯或運行此):

FluentIterable.from(continentList) 
    .transform(new Function<Continent, Void>() { 
     public Void apply(Continent continent) { 
      return FluentIterable.from(continent.getCountries()) 
       .transform(new Function<Country, Void>() { 
        public Void apply(Country country) { 
         return FluentIterable.from(country.getCities()) 
          .transform(new Function<City, Void>() { 
           public Void apply(City city) { 
            // do stuff with city object 
            return null; 
           } 
          }); 
        } 
       }); 
     } 
    }); 

現在問自己:你想維護哪個?哪一個會是最高效的?

番石榴的功能性成語有一些有效的用例。替換Java for循環,甚至嵌套for循環,都不是其中之一。

+4

使用['FluentIterable.transformAndConcat()'](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/FluentIterable.html),您可以鏈接轉換,而不是但是,嵌套它們。 –

+0

@FrankPavageau:是的。但即使這樣會更清潔一點,它仍然會比嵌套循環更醜陋,可讀性更差。 – ig0774

+0

@ ig0774感謝您的提示:) – GobiasKoffi

3

另一個怪物,使用AbstractIterator:

class CityIterable implements Iterable<City> { 
     List<Continent> continents; 

     CityIterable(List<Continent> continents) { 
      this.continents = continents; 
     } 

     @Override 
     public Iterator<City> iterator() { 
      return new AbstractIterator<City>() { 
       Iterator<Continent> continentIterator = continents.iterator(); 
       Iterator<Country> countryIterator; 
       Iterator<City> cityIterator; 

       @Override 
       protected City computeNext() { 
        if (cityIterator != null && cityIterator.hasNext()) { 
         return cityIterator.next(); 
        } 
        if (countryIterator != null && countryIterator.hasNext()) { 
         cityIterator = countryIterator.next().getCities().iterator(); 
         return computeNext(); 
        } 
        if (continentIterator.hasNext()) { 
         countryIterator = continentIterator.next().getCountries().iterator(); 
         return computeNext(); 
        } 
        return endOfData(); 
       } 
      }; 
     } 
    } 

然後調用它:

for (City city: new CityIterable(continentList)) { 
     System.out.println(city.name); 
    } 

鑑於這種monstruosity,如何遵循ig0774的意見和保持嵌套循環

P.S.無需過濾器。

+0

感謝您的提示:) – GobiasKoffi

1

我同意其他人的看法,即嵌套循環是最有效的方法。不過我想每一個循環級解壓到一個單獨的方法既保持可讀性,確保每個方法不正是一件事情:

public void doStuffWithWorld(World world){ 
    for (Continent continent : world.getContinents()) { 
     doStuffWithContinent(continent); 
    } 
} 

private void doStuffWithContinent(Continent continent) { 
    for (Country country : continent.getCountries()) { 
     doStuffWithCountry(country); 
    } 
} 

private void doStuffWithCountry(Country country) { 
    for(City city : country.getCities()){ 
     doStuffWithCity(city); 
    } 
} 

private void doStuffWithCity(City city) { 
    // do stuff here 
} 

如果你需要通過不同層次進行一些狀態,你有幾個選項:將它們放入包含類的成員字段中,將第二個參數傳遞給可以是地圖或自定義對象的所有方法。

+0

我真的很喜歡你的建議;如果需要的話,我也會單元測試每個單獨的活套功能。謝謝 :) – GobiasKoffi

8

您可以定義靜態函數爲:
•的getCountries()在大陸,大陸或功能
•getCities()在國家,國家或功能

現在你可以這樣做......

FluentIterable.from(continentList) 
    .transformAndConcat(Continent.getCountriesFunction()) 
    .transformAndConcat(Country.getCitiesFunction()) 
    . //filter //tranform //find //toList() //etc. 

如果:
•您經常使用這種(更多)番石榴。
•對你定義函數和謂詞的位置有一定的規則/想法。
•並且有不同(複雜)的東西要過濾或搜索。
然後它可以是一個偉大的恩惠,並可以使許多情況下更容易。我知道我很高興我做到了。

如果你使用它稀疏,那麼我將不得不同意@Louis Wasserman。然後它不值得這個麻煩。另外,將函數和謂詞定義爲一個匿名的內部類,就像其他的例子一樣......真的很難看。