Lazily初始化集合

时间:2009-04-20 17:44:32

标签: java collections

懒惰地初始化集合的最佳方法是什么,我特别关注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。

延迟初始化的目的纯粹与性能有关。

我很想知道其他人提出的方法是什么。有什么想法吗?

8 个答案:

答案 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>();
}

因为就目前而言,如果多个线程同时阻塞,那么你将会破坏苹果。