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 评论的那样,这几乎肯定会像嵌套循环一样简单。此外,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 for 循环,甚至是嵌套的 for 循环,都不是其中之一。

于 2013-01-15T15:39:16.283 回答
8

您可以为以下各项定义静态函数:
• Continent、Continents 或 Functions 中的 getCountries()
• Country、Countrys 或 Functions 中的 getCities()

现在你可以做类似...

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

如果:
• 你经常像这样(更多)使用番石榴。
• 对定义函数和谓词的位置有一定的规则/想法。
• 并且有不同(复杂)的东西要过滤或搜索。
然后它可以是一个很大的好处,可以使许多情况变得容易一些。我知道我很高兴我做到了。

如果您很少使用它,那么我将不得不同意@Louis Wasserman。那么麻烦就不值得了。此外,像其他示例一样将函数和谓词定义为匿名内部类......真的很难看。

于 2013-08-13T10:09:24.867 回答
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 的建议并保留嵌套循环

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

我同意其他人的观点,因为嵌套循环是最有效的方法。但是:我会将每个循环级别提取到一个单独的方法中,以保持可读性并确保每个方法都只做一件事:

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
}

如果您需要通过不同的级别携带一些状态,您有几个选择:将它们放在包含类的成员字段中,将第二个参数传递给可以是映射或自定义对象的所有方法。

于 2013-01-16T08:19:40.610 回答