Подтвердить что ты не робот

Итераторы Guava и итерация по списку в объекте списка

У меня есть следующий пример кода, который состоит из 3-вложенных for-loops.

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

Есть ли способ подражать этому вложенному циклу с использованием Guava и итераторов? Я пытался найти подходящий пример без большой удачи, и мне было интересно, может ли кто-нибудь помочь мне? Мой коллега упомянул использование фильтров.

EDIT: исправлена ​​крошечная ошибка в примере кода

4b9b3361

Ответ 1

Как заметил Питер Лоури, это почти наверняка будет проще, чем вложенные циклы. Более того, документация Guava дает следующее предупреждение:

Императивный код должен быть вашим по умолчанию, ваш первый выбор по сравнению с Java 7. Вам не следует использовать функциональные идиомы, если вы не уверены в одном из следующих:

  • Использование функциональных идиом приведет к чистой экономии строк кода для всего проекта. Перемещение определения функции другому файлу или константе не помогает.
  • Для эффективности вам нужен лениво вычисляемый вид преобразованного и не может рассчитывать на явно вычисленную коллекцию. Кроме того, вы прочитали и перечитали Effective Java, пункт 55, и помимо этих инструкций, вы действительно сделали бенчмаркинг, чтобы доказать, что эта версия быстрее, и может привести числа, чтобы доказать это.

Пожалуйста, убедитесь, что при использовании функциональности Guava коммунальные услуги, что традиционный императивный способ делать вещи не более читаемым. Попробуйте написать его. Это было так плохо? Было ли это больше читаемый, чем нелепо неудобный функциональный подход, которым вы были собирается попробовать?

Однако, если вы настаиваете на игнорировании рекомендаций, вы можете использовать что-то вроде этого чудовища (обратите внимание, что я на самом деле не пытался его компилировать или запускать):

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

Теперь спросите себя: что бы вы хотели сохранить? Что будет наиболее эффективным?

Существуют действительные прецеденты для функциональной идиомы Guava. Замена Java для циклов, даже вложенных для циклов, не является одним из них.

Ответ 2

Вы можете определить статические функции для:
• getCountries() на континенте, континентах или функциях
• getCities() в стране, странах или функциях

Теперь вы можете сделать что-то вроде...

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

Если:
• Вы часто используете Guava (чаще).
• И иметь определенные правила/мысли, где вы определяете свои функции и предикаты.
• И иметь различные (сложные) вещи для фильтрации или поиска.
Тогда это может быть отличным благом и может сделать многие ситуации довольно легкими. Я знаю, что я рад, что сделал. Если вы используете его редко, тогда мне придется согласиться с @Louis Wasserman. Тогда это не стоит хлопот. Кроме того, определение функций и предикатов как анонимного внутреннего класса, например, других примеров... действительно уродливо.

Ответ 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);
    }

Учитывая, как эта монстра, следуйте советам ig0774 и сохраняйте вложенные циклы.

P.S. Нет необходимости в фильтрах.

Ответ 5

Я согласен с другими в том, что вложенные циклы являются наиболее эффективным способом. Тем не менее: я бы выделил каждый уровень цикла на отдельный метод, чтобы поддерживать читаемость и убедиться, что каждый метод выполняет только одну вещь:

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
}

И если вам нужно переносить какое-либо состояние через разные уровни, у вас есть несколько вариантов: поместите их в поля-члены содержащего класса, передайте второй параметр ко всем методам, которые могут быть либо картой, либо настраиваемым объектом.