2013-01-15 16 views
13

Mam następujący kod przykładowy, który składa się z 3-zagnieżdżonych pętli for.Guer iteratory i iteracja na liście wewnątrz obiektu listy

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

Czy istnieje sposób naśladowania tej zagnieżdżonej pętli for za pomocą Guavy i iteratorów? Próbowałem znaleźć odpowiedni przykład bez większego szczęścia i zastanawiałem się, czy ktoś może mi pomóc? Mój współpracownik wspomniał o używaniu filtrów.

EDIT: Poprawiono drobny błąd w przykładowym kodem

+3

Można zagnieździć swoje mapowania. IMHO prawdopodobnie będzie prostsze jak pętle zagnieżdżone, przynajmniej dla zewnętrznych pętli. –

+3

W linii 3, nie powinno to być "kontynent.getCountries()"? – Chris

+0

Możesz użyć guavas "transformacja" i "concat", aby utworzyć pojedynczą listę trzech kontynentów, kontynentu, miasta, miasta, a następnie powtórz to, ale przynajmniej z Javą 7 kod będzie raczej brzydki. Zostałbym z zagnieżdżonymi pętlami. – Chris

Odpowiedz

11

Jak komentuje Peter Lawrey, to prawie na pewno będzie prostsze jak zagnieżdżonych pętli. Więcej skończy, Guava documentation daje to ostrzeżenie:

kod Imperatyw powinien być domyślnym, pierwszym wyborem jako Java 7. Nie należy używać idiomów funkcjonalne, chyba że jesteś absolutnie pewny jednego z następujących powodów:

  • Używanie funkcjonalnych idiomów spowoduje oszczędności netto dla linii kodu dla całego projektu. Przeniesienie definicji funkcji funkcji do innego pliku lub stałej nie pomaga.
  • Aby uzyskać wydajność, potrzebny jest leniwie wyliczony widok przekształconej kolekcji i nie można uzyskać rozliczenia dla jawnie wyliczonej kolekcji. Dodatkowo, przeczytałeś i ponownie przeczytałeś Efektywną Javę, poz. 55 i , poza wykonaniem tych instrukcji, wykonałeś testowanie , aby udowodnić, że ta wersja jest szybsza, i możesz przytoczyć numery , aby to udowodnić.

Pamiętaj, przy użyciu narzędzi funkcjonalnych guawy jest, że tradycyjna imperatyw sposób robienia rzeczy nie jest bardziej czytelny. Spróbuj to napisać. Czy to było takie złe? Czy było to łatwiejsze do odczytania niż absurdalnie niezręczne podejście funkcjonalne, o które się starałeś?

Jednakże, jeśli jesteś natarczywe na ignorując rady, można użyć coś jak to monstrum (zauważ, że nie zostały faktycznie starał się skompilować lub uruchomić tego):

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; 
           } 
          }); 
        } 
       }); 
     } 
    }); 

Teraz należy zadać sobie pytanie: Które chciałbyś zachować? Który z nich będzie najbardziej wydajny?

Istnieją ważne przypadki użycia dla idiomu funkcjonalnego Guawy. Zastępowanie Java dla pętli, nawet zagnieżdżonych dla pętli, nie jest jednym z nich.

+4

Używając ['FluentIterable.transformAndConcat()'] (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/FluentIterable.html), możesz połączyć transformacje zamiast ich gniazdowanie. –

+0

@FrankPavageau: True. Ale nawet jeśli to będzie trochę czystsze, nadal będzie brzydsze i mniej czytelne niż zagnieżdżone pętle. – ig0774

+0

@ ig0774 Dzięki za pro-napiwek :) – GobiasKoffi

3

Innym potworność, używając 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(); 
       } 
      }; 
     } 
    } 

Następnie nazywając go:

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

Biorąc pod uwagę jak to monstruosity, wykonaj porady ig0774 i zachować zagnieżdżone pętle.

P.S. Nie potrzebujesz filtrów.

+0

Dzięki za pro-napiwek :) – GobiasKoffi

1

Zgadzam się z innymi, że zagnieżdżone pętle są najbardziej efektywną drogą. Jednakże: chciałbym wyodrębnić każdy poziom pętli do osobnej metody zarówno utrzymać czytelność i upewnić się, że każda z tych metod ma dokładnie jedno:

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 
} 

A jeśli trzeba przeprowadzić pewne państwa za pośrednictwem różnych poziomach, trzeba kilka opcji: umieść je w polach elementu klasy zawierającej, przeprowadź drugi parametr dla wszystkich metod, które mogą być mapą lub niestandardowym obiektem.

+0

Bardzo podoba mi się Twoja sugestia; to także ja, aby w razie potrzeby przetestować każdą indywidualną funkcję looper. Dzięki :) – GobiasKoffi

8

Można zdefiniować funkcje statyczne:
• metodę: getCountries() in kontynentu kontynentów lub funkcji
• GetCities() w kraju, krajów lub funkcji

Teraz można zrobić coś takiego ...

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

Jeśli:
• użyć Guava tak (więcej) często.
• I mieć pewne zasady/przemyślenia nad tym, gdzie definiujesz swoje Funkcje i Predykaty.
• I różne (złożone) rzeczy do filtrowania lub wyszukiwania.
To może być dobra okazja i może ułatwić wiele sytuacji. Wiem, że cieszę się, że to zrobiłem.

Jeśli użyjesz go skąpo, to muszę się zgodzić z @Louis Wasserman. Wtedy nie jest to warte kłopotów. Ponadto definiowanie funkcji i predykatów jako anonimowej klasy wewnętrznej, podobnie jak inne przykłady ... jest naprawdę brzydkie.

Powiązane problemy