代码之家  ›  专栏  ›  技术社区  ›  CPerkins

类导出两个集合上的迭代器

  •  3
  • CPerkins  · 技术社区  · 10 年前

    考虑类包含两个集合的情况。是否可以在两个集合上提供迭代器 以调用方可以用来迭代的方式?

    我的简单示例:

    public class Bar {
        public static class Beer { /* ... */ }
        public static class Wine { /* ... */ }
        private Set<Beer> beers = new HashSet<Beer>();
        private Set<Wine> wines = new HashSet<Wine>();
    
        public Iterator<Beer> beerIterator() { return beers.iterator(); }
        public Iterator<Wine> wineIterator() { return wines.iterator(); }
    }
    

    到目前为止,一切都很好。我们可以声明返回迭代器的方法,但按照我尝试的方式,调用者不能使用迭代器进行迭代。

    void caller(Bar bar) {
        for (Beer beer: bar.beerIterator()) { // <-- Compilation error: Can only iterate over an array or an instance of java.lang.Iterable
        }
    }
    

    有什么建议吗?

    2 回复  |  直到 10 年前
        1
  •  2
  •   Aivean    10 年前

    如果返回迭代器的目的是保护您的集合不受更改,同时让客户端能够使用 foreach 循环,则最明确的方法是使用 Collections.unmodifiableSet 包装。您可以将其返回为 Iterable 接口以进一步隐藏实现。

    public static class Bar {
        public static class Beer { /* ... */ }
        public static class Wine { /* ... */ }
        private Set<Beer> beers = new HashSet<Beer>();
        private Set<Wine> wines = new HashSet<Wine>();
    
        public Iterable<Beer> beerIterable() { return Collections.unmodifiableSet(beers); }
        public Iterable<Wine> wineIterable() { return Collections.unmodifiableSet(wines); }
    }
    
    public static void main(String[] args) {
        for (Bar.Beer beer : new Bar().beerIterable()) {
    
        }
    }
    

    这种方法比@Tim Biegeleisen建议的方法要好,因为它可以保护您的藏品不被外界更改。当你回来时 .iterator 对于原始集合,客户端仍然可以通过调用 remove() 方法正在换行 unmodifiableSet 防止了这种情况。

    但是,请注意,客户端仍然可以修改 Beer Wine 在迭代期间,如果它们是可变的。若你们想完全保护自己不受更改的影响,你们需要在将你们的收藏归还给客户之前,对它们进行深度防御。

        2
  •  0
  •   Tim Biegeleisen    10 年前

    诀窍是在内部定义2个内部类 Bar 它返回啤酒和葡萄酒的自定义迭代器:

    public class Bar {
        public static class Beer { /* ... */ }
        public static class Wine { /* ... */ }
        private Set<Beer> beers = new HashSet<Beer>();
        private Set<Wine> wines = new HashSet<Wine>();
    
        private class Beers implements Iterable<Beer> {
            @Override
            public Iterator<Beer> iterator() {
                return beers.iterator();
            }
        }
    
        private class Wines implements Iterable<Wine> {
            @Override
            public Iterator<Wine> iterator() {
                return wines.iterator();
            }
        }
    
        public Beers beers() {
            return new Beers();
        }
    
        public Wines wines() {
            return new Wines();
        }
    }
    

    您可以像这样使用自定义迭代器:

    Bar bar = new Bar();
    // add some beers and wines here
    
    for (Beer beer : bar.beers()) {
        System.out.println("Found another beer: " + beer);
    }
    
    for (Wine wine : bar.wines()) {
        System.out.println("Found another wine: " + wine);
    }