14

3 ネストされた for ループで構成される次のサンプル コードがあります。

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

Guava とイテレータを使用して、このネストされた for ループを模倣する方法はありますか? 私は運があまり良くない適切な例を見つけようとしてきました.誰かが私を助けてくれるかどうか疑問に思っていました. 私の同僚は、フィルターの使用について言及しました。

編集: サンプルコードの小さなバグを修正

4

5 に答える 5

12

Peter Lawrey がコメントしたように、これはネストされたループとしてより単純になることはほぼ確実です。さらに、グアバのドキュメントには次の警告が表示されます。

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 の for ループを置き換えることは、ネストされた for ループであっても、その 1 つではありません。

于 2013-01-15T15:39:16.283 に答える
3

AbstractIteratorを使用したもう1つの怪物:

    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のアドバイスに従い、ネストされたループを維持します

PSフィルターは必要ありません。

于 2013-01-15T15:56:50.930 に答える
2

いいえ、簡単な方法はありません。また、質問の for-each ループよりも冗長になります。

http://code.google.com/p/guava-libraries/issues/detail?id=218#c5とhttp://code.google.com/p/guava-libraries/wiki/FunctionalExplainedの警告を参照してください

于 2013-01-15T15:31:14.583 に答える
1

ネストされたループが最も効率的な方法であるという点で、私は他の人に同意します。ただし、各ループレベルを個別のメソッドに抽出して、読みやすさを維持し、各メソッドが正確に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
}

また、さまざまなレベルで何らかの状態を保持する必要がある場合は、いくつかのオプションがあります。それらを含むクラスのメンバー フィールドに配置し、マップまたはカスタム オブジェクトのいずれかであるすべてのメソッドに 2 番目のパラメーターを渡します。

于 2013-01-16T08:19:40.610 に答える