从Iterable

时间:2016-08-13 02:56:12

标签: java performance list memory-management

我发现自己经常做以下事情:

Iterator<A> itr = iterableOfA.getIterator();
List<B> list = new ArrayList<>(); // how about LinkedList?
while (itr.hasNext()) {
    B obj = iter.next().getB();
    list.add(obj);
}
someMethod(list); // this method takes an Iterable

我不知道iterableOfA中可能有多少元素 - 可能是5,可能是5000.在这种情况下,LinkedList是一个更好的实现在这里使用(因为{ {1}}那么是O(1))?就目前而言,如果list.add(obj)有5000个元素,这将导致许多iterableOfA支持数组的重新生成。

其他选择是:

list

这意味着Iterator<A> itr = iterableOfA.getIterator(); int size = Iterables.size(iterableOfA); // from Guava List<B> list = new ArrayList<>(size); // and the rest... 的双重迭代。当迭代的大小是未知的并且可能变化很大时,哪个选项最好:

  1. 只需使用iterableOfA
  2. 只需使用ArrayList
  3. 计算LinkedList中的元素并分配iterableOfA
  4. 编辑1

    澄清一些细节:

    1. 我主要针对性能进行优化,其次是内存使用。
    2. ArrayList是一个短期分配,因为在请求结束时没有代码应该持有对它的引用。
    3. 编辑2

      对于我的具体情况,我意识到list没有处理超过200个元素的迭代,所以我决定选择适合我的someMethod(list)

      但是,在一般情况下,我更愿意实现已接受答案中概述的解决方案(包装在自定义迭代中,无需分配列表)。

      所有其他答案都提供了宝贵的见解,了解new ArrayList<>(200)ArrayList的对比情况,因此,代表一般的SO社区,我感谢大家!

3 个答案:

答案 0 :(得分:2)

  

当迭代的大小是未知的并且可能变化很大时,哪个选项最好

这取决于你的优化目标。

  • 如果您正在优化性能,那么使用ArrayList可能会更快。即使ArrayList需要调整后备数组的大小,它也会使用指数增长模式来实现。但是,它取决于迭代的开销。

  • 如果您要针对长期内存使用进行优化,请考虑使用ArrayList后跟trimToSize()

  • 如果您正在针对峰值内存使用进行优化,那么&#34;先计算&#34;方法可能是最好的。 (这假设你可以迭代两次。如果迭代器实际上是一个懒惰计算的包装器......这可能是不可能的。)

  • 如果您要优化以减少GC,那么&#34;先计算&#34;可能是最好的,取决于迭代的细节。

在任何情况下,您都会被建议:

  1. 在之前为您的应用配置文件,您将花费更多时间在此问题上。在很多情况下,您会发现这不值得您进行优化。

  2. 使用应用程序中的类和典型数据结构对您正在考虑的两个备选方案进行基准测试。

  3.   

    按照目前的情况,如果iterableOfA有5000个元素,这将导致许多列表的后备数组的重新调整。

    ArrayList类调整为与当前大小成比例的新大小。这意味着结果数量为O(logN)N列表追加调用的总费用为O(N)

答案 1 :(得分:1)

第三种选择也不错。为了获得大小,大多数集合只返回它们在内部维护的计数器......它不会遍历整个列表。这取决于实现,但所有java.util.xxx集合类都是这样做的。

如果您知道&#34; iterableOfA&#34;的潜在类型,您可以检查它们的大小。

如果&#34; iterableOfA&#34;将是一些自定义实现,你不确定如何完成大小,链表将更安全。这是因为你的尺寸变化,调整大小的可能性更高,因此你将无法获得可预测的性能。

也不确定您正在填写的集合中正在执行哪些操作&#34; B&#34;,您的选择也取决于此。

答案 2 :(得分:1)

我会完全跳过将元素复制到新集合。

我们有实用程序代码,可以轻松地将Iterators包装到Iterables和Filter中,以便在类型之间进行转换,但它的要点是:

final Iterable<A> iofA ... ;
Iterable<B> iofB = new Iterable<B>() {
  public Iterator<B> iterator() {
    return new Iterator<B>() {
      private final Iterator<A> _iter = iofA.iterator();
      public boolean hasNext() { return _iter.hasNext(); }
      public B next() { return _iter.next().getB(); }
    };
  }
};

无需额外存储等。