【问题标题】:java - live view on collection contained within a collection contained within ... etcjava - 包含在...等中的集合中包含的集合的实时视图
【发布时间】:2011-08-26 19:33:56
【问题描述】:

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

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

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

非常感谢所有 cmets!

【问题讨论】:

  • 你能贴一些代码sn-p吗?
  • 你真的需要所有的D吗?也许您可以使用责任链模式通过您的对象层次结构分发所有 D 上的所有方法调用,而无需收集所有 D。

标签: java collections guava


【解决方案1】:

实际上,我认为 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;
    }
}

【讨论】:

    【解决方案2】:

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

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

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

    您还可以使用树来存储对象,尤其是在它们相当简单的情况下。例如,您可以创建一个 XML DOM 对象并使用 XPath 表达式提取您想要的 D 子集。这将允许对您感兴趣的对象集进行更动态的访问。

    每个解决方案在设置成本、维护成本、结果及时性、使用灵活性和获取结果成本方面都有不同的权衡。您应该选择哪个取决于您的上下文。

    【讨论】:

      【解决方案3】:

      这不是很有效。

      在内存中迭代非常快。此外,创建 10 k 个元素的 ArrayList 与创建 10 个 ArrayList 每个 1k 个元素的效率不会有太大的不同。因此,总而言之,您可能应该首先进行最直接的迭代。很有可能这工作得很好。

      即使你有无数元素,实现一个直接的迭代进行比较可能是明智的。否则你不知道你是否能够优化,或者你是否通过聪明地做事来减慢速度。

      话虽如此,如果您想针对所有 D 的顺序读取访问进行优化,我会在外部维护一个“索引”。根据您的情况,索引可能是LinkedListArrayListTreeList 等。例如,如果您不确定索引的长度,最好避免使用ArrayList。如果您想使用该元素的引用有效地删除随机元素,OrderedSet 可能比列表等要好得多。

      当您这样做时,您必须担心类中索引和实际引用的一致性。 IE。更复杂=更多隐藏错误的地方。所以,除非你通过性能测试发现有必要,否则真的不建议尝试优化。

      (顺便说一句,除非您在谈论 EXTREME 高性能代码,否则避免实例化新集合对象不太可能使事情变得更快。现代 JVM 中的对象实例化只需要几十纳秒左右。此外,您可能会错误地使用一个 ArrayList 具有较小的初始长度或其他东西并使事情变得更糟)

      【讨论】:

        【解决方案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;
            }
        }
        

        如果您的目标只是对 D 进行迭代并且您实际上并不需要 collection 视图,则此方法非常有效。它避免了大型临时集合的实例化。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-12-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-17
          • 1970-01-01
          • 2012-03-25
          相关资源
          最近更新 更多