懒惰地初始化集合的最佳方法是什么,我特别关注Java。我看到有些人决定在修改方法中做这件事(看起来有点令人讨厌),如下所示:
public void addApple(final Apple apple) {
if (this.apples == null) {
apples = new LinkedList<Apple>();
}
this.apples.add(apple);
}
您可以将初始化重构为方法并从添加/删除/更新等方式调用它......但它似乎有点糟糕。人们也通过以下方式揭露了这个集合本身,这通常会加剧这一点:
public Collection<Apple> getApples() {
return apples;
}
打破封装并导致人们直接访问Collection。
延迟初始化的目的纯粹与性能有关。
我很想知道其他人提出的方法是什么。有什么想法吗?
答案 0 :(得分:3)
我将惰性实例化放入给定函数的getter中。通常我懒惰地实例化一个列表,以避免DB命中,如果可能的话。例如:
public final Collection<Apple> getApples() {
if (apples == null) {
// findApples would call the DB, or whatever it needs to do
apples = findApples();
return apples;
}
public void addApple(final Apple apple) {
//we are assured that getApples() won't return
//null since it's lazily instantiated in the getter
getApples().add(apple);
}
这种方法意味着其他函数(比如removeApples())也不需要担心实例化。他们也会调用getApples()。
答案 1 :(得分:3)
为了在多线程环境中安全地懒惰地初始化成员,您需要一些并发机制来使初始化成为原子并且对其他线程可见。每次访问延迟初始化成员时,都会在初始化和期间支付此费用。持续的费用可能会严重影响业绩。分析延迟初始化的效果非常重要。正确的选择将根据应用程序的不同而有很大差异。
答案 2 :(得分:1)
您可以将初始化重构为方法,并从添加/删除/更新
中调用它
这就是我要做的,但我会将方法设为私有/受保护
protected Collection<Apple> getApples() {
if (apples == null) {
apples = new LinkedList<Apple>();
}
return apples;
}
答案 3 :(得分:1)
对于add case,我会说你在你的constuctor中初始化它,并且无意中直接暴露了这个集合,你可能会考虑:
public Collection<Apple> getApples() {
return Collections.unmodifiableCollection(apples);
}
让人们不要使用吸气剂来做更多事情。
答案 4 :(得分:1)
第二个选项是好的:
初始化为方法并从添加/删除/更新
调用它
private Collection<Apple> apples;
....
private final Collection<Apple> getApples() {
if( apples == null ) {
apples = new LinkedList<Apple>();
}
return apples;
}
public final void addApple( Apple a ) {
getApples().add( a );
}
public final void removeApple( Apple a ) {
getApples().remove( a );
}
public Iterator<Apple> iterator() {
return getApples().iterator;
}
...因为人们也通过......
公开了这个集合
没有懒惰的初始化策略可以防止这种情况发生。如果您不希望公开底层集合(这是一个非常好的主意),您必须将其设为私有,使其成为最终(如上面的示例)并提供迭代器来访问集合的元素,而不是把它自己传递给它。
我想你已经拥有它了。
答案 5 :(得分:0)
首先,您必须确定函数调用的开销是否值得。我的猜测是你担心内存占用而不是原始速度。
首先我要加上这个。请注意,它属于班级
private Collection<Apple> myApples() {
if (this.apples == null) {
this.apples = new LinkedList<Apple>();
}
return this.apples;
}
现在你的addApples看起来像这样。
public void addApple(final Apple apple) {
myApples.add(apple);
}
以及在课堂内部使用该集合的任何其他内容。
答案 6 :(得分:0)
我会在setter中保持延迟初始化。如果后备集合为空,则getter可以简单地返回一个空集合。
public Collection<Apple> getApples() {
return apples==null ? Collections.emptyList() : apples;
}
这样,如果在没有调用setter的情况下调用getter,则永远不会实例化后台集合。
答案 7 :(得分:0)
将它放在synchronized块中,或通过其他机制(例如compareAndSet,并发容器等)使其成为线程安全的:
if (this.apples == null) {
apples = new LinkedList<Apple>();
}
因为就目前而言,如果多个线程同时阻塞,那么你将会破坏苹果。