繁体   English   中英

在构造函数中设置Java Collection的大小更好吗?

[英]Is it better to set size of a Java Collection in constructor?

如果我知道当时的大小,最好将Collection的大小传递给Collection构造函数吗? 在扩展Collection和分配/重新分配方面的节省效果是否显着?

如果我知道Collection最小大小但不知道上限,该怎么办。 至少以最小的尺寸创建它仍然值得吗?

不同的集合对此有不同的性能影响,对于ArrayList而言,节省是非常明显的。

import java.util.*;
public class Main{
public static void main(String[] args){
  List<Integer> numbers = new ArrayList<Integer>(5);
  int max = 1000000;
  // Warmup
  for (int i=0;i<max;i++) {
    numbers.add(i);
  }

  long start = System.currentTimeMillis();
  numbers = new ArrayList<Integer>(max);
  for (int i=0;i<max;i++) {
    numbers.add(i);
  }
  System.out.println("Preall: "+(System.currentTimeMillis()-start));

  start = System.currentTimeMillis();
  numbers = new ArrayList<Integer>(5);
  for (int i=0;i<max;i++) {
    numbers.add(i);
  }
  System.out.println("Resizing: "+(System.currentTimeMillis()-start));

}
}

结果:

Preall: 26
Resizing: 58

如果将max设置为10000000的值的10倍,则得出:

Preall: 510
Resizing: 935

因此,即使大小不同,您也可以看到比率保持不变。

这几乎是最坏的测试,但是一次填充一个元素一个数组很常见,您会发现速度大约相差2倍。

所有集合都是自动扩展的。 不知道边界不会影响其功能(直到遇到其他问题,例如使用所有可用内存等),但是可能会影响其性能。

有一些收藏。 最值得注意的是ArrayList,因为要复制整个基础数组,所以自动扩展的成本很高。 数组列表的默认大小为10,每次达到最大值时,其大小都会增加一倍。 因此,假设您知道您的arraylist将包含110个对象,但不指定其大小,则将发生以下复制

复制10-> 20
复制20-> 40
复制40-> 80
复制80-> 160

通过预先告诉arraylist它包含110个项目,您可以跳过这些副本。

有根据的猜测总比没有好

即使您错了也没关系。 集合仍将自动展开,并且您仍将避免某些副本。 降低性能的唯一方法是,如果您的猜测太大了:这将导致分配给集合的内存过多

好,这是我的jmh代码:

@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(3)
public class Comparison
{
  static final int size = 1_000;
  @GenerateMicroBenchmark
  public List<?> testSpecifiedSize() {
    final ArrayList<Integer> l = new ArrayList(size);
    for (int i = 0; i < size; i++) l.add(1);
    return l;
  }

  @GenerateMicroBenchmark
  public List<?> testDefaultSize() {
    final ArrayList<Integer> l = new ArrayList();
    for (int i = 0; i < size; i++) l.add(1);
    return l;
  }
}

我的size = 10_000结果:

Benchmark             Mode Thr    Cnt  Sec         Mean   Mean error    Units
testDefaultSize       avgt   1      9    1       80.770        2.095  usec/op
testSpecifiedSize     avgt   1      9    1       50.060        1.078  usec/op

size = 1_000结果:

Benchmark             Mode Thr    Cnt  Sec         Mean   Mean error    Units
testDefaultSize       avgt   1      9    1        6.208        0.131  usec/op
testSpecifiedSize     avgt   1      9    1        4.900        0.078  usec/op

我的解释:

  • presizing对默认大小一些边缘;
  • 边缘不是那么壮观;
  • 花在添加到列表上的绝对时间是微不足道的。

我的结论是:

如果增加初始大小,则可以使您的心脏感到温暖,但客观地说,客户不太可能注意到差异。

在少数情况下,大小是众所周知的(例如,当将已知数量的元素填充到新集合中时),出于性能原因可以进行设置。

通常,最好省略它,而改用默认构造函数,这样可以使代码更简单和更好理解。

对于基于数组的集合,重新调整大小是一项非常昂贵的操作。 这就是为什么为ArrayList传递准确大小是一个好主意的原因。

如果将大小设置为最小大小( MIN ),然后将MIN +1个元素添加到集合中,则需要重新调整大小。 ArrayList()调用ArrayList(10)因此如果MIN足够大,那么您将获得一些好处。 但是最好的方法是使用期望的集合大小创建ArrayList

但是可能您更喜欢LinkedList,因为它没有添加元素的任何开销(尽管list.get(i)开销为O(i))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM