Java中初始化ArrayList字段的最佳实践

6

在Java中初始化ArrayList字段的最佳实践是什么(以避免测试空值)?

在声明时,像这样:

private List<String> myList = new ArrayList<String>();

或者在getter中,像这样:
public List<String> getMyList() {
    if(myList == null) {
        myList = new ArrayList<String>();
    }
    return myList;
}

或者,在构造函数中:

public Test(){
    myList = new ArrayList<String>();
}

也许这是相同的,但我很想知道。

1
第一个。懒加载被高估了,而且更容易出现线程安全问题。 - Andy Turner
@cricket_007,我为构造函数选项编辑了我的问题。 - JavaDev
3个回答

9
第一项选项允许您执行一个
private final List<String> myList = new ArrayList<>();

因此,这可以防止您在以后意外创建全新的列表;从而有助于解决(许多,但不是所有)多线程问题。除此之外,现在编译器可以帮助您确保您的字段被初始化仅一次
此外,第二个选项可以被视为“延迟初始化”。从这个意义上说:它可以被看作是“优化选择”!从那里开始,许多人主张避免过早优化!
你知道的,当你不能依赖已经创建的列表时,那会引起很多麻烦。因此,即使从这个角度来看,你也有另一个理由更喜欢选项1!
编辑,关于编译器选项:从语义的角度来看,选项1和选项3(或多或少)是“相等”的[提示:如果您发现选择选项1或选项3在您的代码中有所区别...那将是一个很好的指示,说明您的代码存在严重问题)。
尽管如此,唯一可能有所不同的事情-如果您有一个“依赖注入”构造函数,例如:
public YourClass() { this(new ArrayList<String>); }
YourClass(List<String> incomingList) { myList = incomingList; }

这个解决方案适用于那些您需要“控制”的对象;也就是说,您需要传递模拟对象来启用单元测试。
简而言之:
- 如果可能的话,请优先选择选项1:使用“final”。 - 如果需要依赖注入,请使用选项3。 - 除非有非常好的理由选择它,否则避免选项2。

1
选项3还允许使用final关键字。 - OneCricketeer
你能解释一下为什么在选项1中要使用final吗? - JavaDev
1
@JavaDev 简单地说:因为你可以免费获得我在那里列出的所有东西?抱歉,我不明白你的问题:我认为我已经解释了为什么选项1 + final是“最佳选择”的原因。 - GhostCat
@Jägermeister 我的意思是更倾向于选项1,好的,根据你的解释我同意。但是为什么在这个选项中使用final关键字会更好呢? - JavaDev
1
@JavaDev 我之前这样说是因为那可以防止你重写字段。而且编译器会告诉你忘记初始化字段的情况。长话短说:最好的做法是让所有字段都是final的;只有在有充分理由的情况下才应该违反这个规则。最好使用不可变对象! - GhostCat

1

通常,在getter中进行的惰性初始化比它值得的麻烦多。如果您有一个严重的问题,想要最小化列表使用的内存(因为您预计它在大多数情况下为空),则可以将其初始化为零的初始容量:

private List<String> myList = new ArrayList<String>(0);

在实例变量声明和构造函数中初始化没有实际区别,除了可读性。如果将初始化放在实例声明中,更容易确保所有内容都已初始化;而使用构造函数,则需要将初始化与实例变量匹配以确保覆盖它们。很多现实世界的代码需要与第三方库和框架一起工作。当你创建一个必须与Hibernate或Spring等库或框架一起工作的类时,getter中的延迟初始化是行不通的,因为Spring接管了这些事情的布线,或者因为使用你的类的库出于各种原因会替换掉你的List实现(Hibernate将这样做以实现延迟加载)。因此,使你的getter和setter尽可能简单和直接,以便它们不会使使用它们的第三方代码失效。

为什么要将容量设置为0?默认情况下,空列表的大小不是0吗? - JavaDev
1
@JavaDev:默认值为10。容量是预分配的空间,与大小相对应,后者是列表中元素的数量。 - Nathan Hughes

0

这取决于使用情况,但我更喜欢第一种选项。

private List<String> myList = new ArrayList<String>();

如果您的类预计会重新创建ArrayList,那么第二个选项可能会有一些好处,如果类的用户能够将myList变量设置为null。不过,对我来说,这似乎是不好的做法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接