3

我有一些类的ArrayList字段只是有时使用。我通常像这样初始化这些字段:

private List<Widget> widgets = new ArrayList<>();

我了解使用重载构造函数来设置初始容量,所以我想知道是否应该这样声明这些字段:

private List<Widget> widgets = new ArrayList<>(0);

困境是,如果我用 0 初始化列表,那么列表将始终必须重新初始化自身以添加甚至一个项目。但是,如果我使用默认构造函数,它给出的默认容量为 10,那么我可能有一堆项目(并且可能有很多)正在浪费未使用容量的内存。

我知道你们中的一些人会拒绝询问“多久一次”和“您期望多少项目”,但我真的在寻找“最佳实践”方法。一切都是平等的,是否应该使用(0)有时()使用的列表进行初始化?

始终初始化列表是我们部门的政策,因此我可能不会简单地将列表保留为null,此外,这只会回避问题。

4

6 回答 6

5

过早的优化是万恶之源。- D. Knuth。

这似乎是一种实际上对性能没有任何影响的“性能问题”。一方面,您有多确定这些空列表实际上已被初始化?我怀疑大多数现代编译器会延迟对象的初始化,直到他们确定会调用它们。因此,如果您传递无参数构造函数,除非将某些内容添加到列表中,否则它很可能永远不会被使用。另一方面,如果您使用 0 参数构造函数,它保证它必须调整它使用的每个参数的大小。

这是性能优化的三大定律

  1. 永远不要假设您知道编译后的代码实际上在做什么,或者您可以比编译器更好地进行小型优化。
  2. 永远不要在不使用分析器确定瓶颈所在的情况下进行优化。如果您认为您知道,请参阅规则编号 (1)。
  3. 除非您的应用程序有性能问题,否则不要打扰。然后参考规则(2)。

如果您仍然相信自己了解编译器,请查看以下问题:为什么处理排序数组比处理未排序数组更快?

于 2014-04-15T13:05:22.247 回答
2

如果不总是使用列表,请使用延迟初始化

private List<Widget> widgets;

private List<Widget> getList() {
    if (widgets == null) {
         widgets = new ArrayList<>();
    }
    return widgets;
}
于 2014-04-15T13:01:14.103 回答
1

如果您将其设置为 0,ArrayList则无论如何都必须调整大小,所以实际上您是在自取其辱。唯一可以从显式声明中受益的情况是,如果您已经知道您将在列表中达到的最大界限。

如前所述,这是一个微优化,您更有可能找到其他可以显着改进的东西,而不是ArrayList.

于 2014-04-15T13:00:12.450 回答
0

正如已经说过的,延迟初始化可以通过推迟您必须初始化列表的时刻(并因此选择其初始大小)来帮助您。

如果由于您的部门政策不允许使用 null 初始化对象(对此我觉得没有多大意义)而无法进行延迟初始化,则解决方法可能是将空列表初始化为

List widget = new ArrayList<>(0)

并且仅当(并且如果)您确实需要使用列表时,您才创建一个新的列表对象:

widget = new ArrayList<>(someSize)

并且希望在那一刻您可以知道列表可以达到的最大大小(或至少其数量级)。

我知道,这是一个非常愚蠢的把戏,但它符合您的政策。

于 2014-04-15T13:25:48.643 回答
0

我倾向于不同意这些优化是不好的。如果您声明一个包含 n 个元素的数组列表(如果我没记错的话,默认情况下是第 8 个元素),并且您再放一个,那么数组列表在内部将使其大小增加一倍。当您稍后删除此元素时,列表不会减少

于 2014-04-15T13:03:25.997 回答
0

ArrayList 大量使用处理器缓存,实际上速度非常快,您无需进一步优化它。尽管如此,如果您必须创建数百万个微小的 ArrayList 实例,则可能值得考虑重新设计您的整体设计,而不必担心默认的 AL 容量。

于 2014-04-15T13:03:46.970 回答