java - 包含在......等中的集合中的集合的实时视图

时间:2011-05-16 22:16:49

标签: java collections guava

我有一个A类,它可以包含许多B类实例,而这些实例又可以包含许多C类实例,它们可以包含许多D类实例

现在,在A班我有一个方法getAllD。目前每次调用都会发生很多迭代,并且新创建并返回一个相当大的列表。这可能效率不高。

我想知道如何才能做得更好。这个问题Combine multiple Collections into a single logical Collection?似乎触及了一个类似的话题,但我不确定如何将它应用到我的情况中。

非常感谢所有评论!

4 个答案:

答案 0 :(得分:4)

我会将Iterables.concatIterables.transform合并,以获取Ds的实时视图:

public class A {
    private Collection<B> bs;

    /**
     * @return a live concatenated view of the Ds contained in the Cs
     *         contained in the Bs contained in this A.
     */
    public Iterable<D> getDs() {
        Iterable<C> cs = Iterables.concat(Iterables.transform(bs, BToCsFunction.INSTANCE));
        Iterable<D> ds = Iterables.concat(Iterables.transform(cs, CToDsFunction.INSTANCE));
        return ds;
    }

    private enum BToCsFunction implements Function<B, Collection<C>> {
        INSTANCE;

        @Override
        public Collection<C> apply(B b) {
            return b.getCs();
        }
    }

    private enum CToDsFunction implements Function<C, Collection<D>> {
        INSTANCE;

        @Override
        public Collection<D> apply(C c) {
            return c.getDs();
        }
    }
}


public class B {
    private Collection<C> cs;

    public Collection<C> getCs() {
        return cs;
    }
}

public class C {
    private Collection<D> ds;

    public Collection<D> getDs() {
        return ds;
    }
}

如果你的目标只是迭代Ds并且你真的不需要集合视图,那么这很有效。它避免了大型临时集合的实例化。

答案 1 :(得分:1)

您的问题的答案将取决于您的具体情况。这些集合是静态的还是动态的?你在A中收集的B有多大?您是否只是从A访问Ds,或者您有时希望在树中更远或返回Bs或Cs?您想要从特定A访问同一组D的频率是多少? D(或C或B)可以与超过1 A相关联吗?

如果一切都是动态的,那么提高性能的最佳机会是从Cs到A的父引用,然后每当C的D列表发生变化时更新父代。这样,您可以在A对象中保留Ds集合,并在其中一个C获取新C或删除一个时更新A.

如果一切都是静态的,并且每个A都有一些D集合的重用,那么缓存可能是一个不错的选择,特别是如果有很多B。 A将有一个带有B键和Ds集合值的映射。 getAllDs()方法首先检查地图是否有B的键,如果是,则返回其Ds集合。如果没有,那么它将生成集合,将其存储到缓存映射中,并返回集合。

您还可以使用树来存储对象,特别是如果它们非常简单。例如,您可以创建XML DOM对象并使用XPath表达式来提取所需的Ds子集。这样可以更加动态地访问您感兴趣的对象集。

这些解决方案中的每一个在设置成本,维护成本,结果的及时性,使用灵活性以及获取结果的成本方面都有不同的权衡。您应该选择哪种方式取决于您的背景。

答案 2 :(得分:0)

实际上,我认为Iterables.concat(或来自Apache Commons的IteratorChain)可以适用于您的情况:

class A {
    Collection<B> children;
    Iterator<D> getAllD() {
        Iterator<Iterator<D>> iters = new ArrayList<Iterator<D>>();
        for (B child : children) {
            iters.add(child.getAllD());
        }
        Iterator<D> iter = Iterables.concat(iters);
        return iter;
    }
}
class B {
    Collection<C> children;
    Iterator<D> getAllD() {
        Iterator<Iterator<D>> iters = new ArrayList<Iterator<D>>();
        for (C child : children) {
            iters.add(child.getAllD());
        }
        Iterator<D> iter = Iterables.concat(iters);
        return iter;
    }
}
class C {
    Collection<D> children;
    Iterator<D> getAllD() {
        Iterator<D> iter = children.iterator();
        return iter;
    }
}

答案 3 :(得分:0)

  

这不是很有效。

在内存中迭代非常快。与创建具有1k个元素的10 ArrayList相比,创建10 {k}元素ArrayList的效率也不会有太大差异。因此,总而言之,您可能应该首先进行最直接的迭代。有可能这很好用。

即使您拥有大量元素,但实施直接迭代以进行比较可能也是明智之举。否则你不知道你是否能够优化,或者你是否通过聪明的做事减慢了速度。

话虽如此,如果你想优化所有Ds的顺序读访问,我会在外面保持一个“索引”。索引可以是LinkedListArrayListTreeList等,具体取决于您的具体情况。例如,如果您不确定索引的长度,则避免ArrayList可能是明智的。如果您想使用该元素的引用有效地删除随机元素,OrderedSet可能比列表等更好。

当你这样做时,你必须担心索引的一致性。你班上的实际参考资料。即更复杂=更多隐藏错误的地方。因此,除非您通过性能测试发现它是必要的,否则尝试进行优化是不可取的。

(btw避免实例化新的集合对象不太可能使事情变得更快,除非你在讨论EXTREME高性能代码。现代JVM中的对象实例化只需要几十纳秒或者其他东西。而且,你可能会错误地使用它一个初始长度很小的ArrayList,会让事情变得更糟)