2013-01-15 45 views
13

Tôi có mã ví dụ sau bao gồm 3 vòng lặp lồng nhau.Trình lặp biến dạng, và lặp qua danh sách trong đối tượng danh sách

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

Có cách nào để bắt chước vòng lặp lồng nhau này bằng cách sử dụng Ổi và trình lặp không? Tôi đã cố gắng tìm một ví dụ phù hợp mà không có nhiều may mắn, và tôi đã tự hỏi liệu có ai đó có thể giúp tôi không? Một đồng nghiệp của tôi đã đề cập sử dụng bộ lọc.

CHỈNH SỬA: Lỗi nhỏ cố định trong mã ví dụ

+3

Bạn có thể lồng bản đồ của mình. IMHO nó có thể đơn giản hơn như vòng lặp lồng nhau, cho các vòng ngoài ít nhất. –

+3

Trong dòng 3, không nên là "continent.getCountries()"? – Chris

+0

Bạn có thể sử dụng ổi "biến đổi" và "concat" để tạo một danh sách duy nhất triples và sau đó lặp lại điều đó, nhưng với Java 7 ít nhất, mã sẽ khá xấu xí. Tôi sẽ ở lại với các vòng lặp lồng nhau. – Chris

Trả lời

11

Như Peter Lawrey nhận xét, đây là gần như chắc chắn sẽ được đơn giản như vòng lồng nhau. Hơn nữa, các Guava documentation cho cảnh báo này:

mã bắt buộc nên mặc định của bạn, lựa chọn đầu tiên của bạn như Java 7. Bạn không nên sử dụng thành ngữ chức năng trừ khi bạn hoàn toàn chắc chắn về một trong các cách sau:

  • Sử dụng các thành ngữ chức năng sẽ dẫn đến tiết kiệm ròng các dòng mã cho toàn bộ dự án của bạn. Di chuyển định nghĩa của một hàm sang một tệp khác hoặc một hằng số, không giúp ích gì.
  • Để đạt hiệu quả, bạn cần có chế độ xem được tính toán một cách lười biếng của bộ sưu tập được chuyển đổi và không thể giải quyết cho bộ sưu tập được tính toán rõ ràng. Ngoài ra, bạn đã đọc và đọc lại Java hiệu quả, mục 55 và ngoài việc làm theo các hướng dẫn đó, bạn đã thực sự thực hiện điểm chuẩn để chứng minh rằng phiên bản này nhanh hơn và có thể trích dẫn số để chứng minh điều đó.

Hãy chắc chắn, khi sử dụng các tiện ích của Guava, cách truyền thống bắt buộc để làm mọi thứ không phải là dễ đọc hơn. Hãy thử viết nó ra. Điều đó thật tệ? Có phải đó là nhiều hơn có thể đọc được hơn phương pháp tiếp cận chức năng lúng túng khó xử mà bạn đã chuẩn bị thử không?

Tuy nhiên, nếu bạn đang van lơn trên phớt lờ lời khuyên, bạn có thể sử dụng một cái gì đó giống như con quái vật này (lưu ý tôi đã không thực sự cố gắng để biên dịch hoặc chạy này):

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

Bây giờ hãy tự hỏi: Bạn muốn duy trì cái nào? Mà sẽ là hiệu quả nhất?

Có các trường hợp sử dụng hợp lệ cho thành ngữ chức năng của Guava. Thay thế Java cho vòng lặp, thậm chí lồng nhau cho vòng lặp, không phải là một trong số họ.

+4

Sử dụng ['FluentIterable.transformAndConcat()'] (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/FluentIterable.html), bạn có thể chuỗi các biến đổi thay vì làm tổ họ, mặc dù. –

+0

@FrankPavageau: Đúng vậy. Nhưng ngay cả khi đó sẽ là một chút sạch hơn, nó vẫn sẽ được uglier và ít có thể đọc được hơn so với các vòng lặp lồng nhau. – ig0774

+0

@ ig0774 Cảm ơn bạn đã ủng hộ mẹo này :) – GobiasKoffi

3

Một con quái vật, sử dụng 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(); 
       } 
      }; 
     } 
    } 

Sau đó, gọi đó là:

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

Với cách monstruosity này, hãy làm theo những lời khuyên ig0774 và giữ các vòng lồng nhau.

P.S. Không cần bộ lọc.

+0

Cảm ơn bạn đã ủng hộ :) – GobiasKoffi

1

Tôi đồng ý với những người khác rằng vòng lặp lồng nhau là cách hiệu quả nhất để thực hiện. Tuy nhiên: Tôi muốn trích xuất từng cấp vòng lặp đến một phương pháp riêng biệt cho cả hai duy trì khả năng đọc và chắc chắn rằng mỗi phương pháp thực hiện chính xác một điều:

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 
} 

Và nếu bạn cần phải mang theo một số nhà nước thông qua các cấp độ khác nhau, bạn có một số tùy chọn: đặt chúng trong các trường thành viên của lớp chứa, chuyển một tham số thứ hai cho tất cả các phương thức có thể là bản đồ hoặc đối tượng tùy chỉnh.

+0

Tôi thực sự thích đề xuất của bạn; nó cũng sẽ cũng tôi để đơn vị kiểm tra từng chức năng looper cá nhân nếu cần thiết. Cảm ơn :) – GobiasKoffi

8

Bạn có thể xác định chức năng tĩnh cho:
• getCountries() trong lục địa, châu lục hoặc Chức năng
• getCities() trong quốc gia, quốc gia hoặc Chức năng

Bây giờ bạn có thể làm một cái gì đó giống như ...

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

Nếu:
• Bạn sử dụng ổi như thế này (thường xuyên hơn).
• Và có các quy tắc/suy nghĩ nhất định về nơi bạn xác định các chức năng và chức năng của mình.
• Và có nhiều thứ phức tạp để lọc hoặc tìm kiếm.
Sau đó, nó có thể là một lợi ích tuyệt vời và có thể làm cho nhiều tình huống khá dễ dàng hơn một chút. Tôi biết tôi rất vui vì tôi đã làm.

Nếu bạn sử dụng nó thưa thớt, thì tôi sẽ phải đồng ý với @Louis Wasserman. Sau đó, nó không phải là giá trị phức tạp. Ngoài ra, việc định nghĩa các hàm và Predicates như một lớp bên trong vô danh giống như các ví dụ khác ... thực sự là xấu.

Các vấn đề liên quan