Ленивая инициализация коллекции - PullRequest
2 голосов
/ 20 апреля 2009

Какой лучший способ лениво инициализировать коллекцию, я специально смотрю на Java. Я видел, как некоторые люди решают сделать это в методах модификации (которые кажутся немного отвратительными), а именно:

public void addApple(final Apple apple) {       

    if (this.apples == null) {
        apples = new LinkedList<Apple>();
    }
    this.apples.add(apple);
}

Вы могли бы реорганизовать инициализацию в метод и вызвать его из add / delete / update и т. Д., Но это выглядит немного бред. Это часто усугубляется тем фактом, что люди также выставляют саму коллекцию через:

public Collection<Apple> getApples() {
    return apples;
}

, который нарушает инкапсуляцию и ведет к непосредственному доступу людей к Коллекции.

Целью ленивой инициализации является исключительно производительность.

Мне любопытно посмотреть, какие другие народы предложили подходы для этого. Есть идеи?

Ответы [ 8 ]

3 голосов
/ 20 апреля 2009

Чтобы безопасно лениво инициализировать элемент в многопоточной среде, вам нужен механизм параллелизма, чтобы инициализация была атомарной и видимой для других потоков. Эта стоимость оплачивается как при инициализации , так и каждый раз при доступе к лениво инициализированному участнику. Эти текущие расходы могут значительно снизить производительность. Очень важно профилировать эффект ленивой инициализации. Правильный выбор будет широко варьироваться в зависимости от приложения.

3 голосов
/ 20 апреля 2009

Я поместил ленивый экземпляр в геттер для данной функции. Обычно я создаю список лениво, чтобы избежать попадания в БД, если это возможно. Пример:

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 голос
/ 20 апреля 2009

Второй вариант в порядке:

Инициализация в метод и вызов его из add / delete / update

 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;
  }

... из-за того, что люди также выставляют саму коллекцию через ...

Никакая ленивая стратегия инициализации не может этому помешать. Если вы не хотите, чтобы лежала лежащая в основе коллекция (что очень хорошая идея), вы должны сделать ее приватной, сделать ее окончательной (как в примере выше) и предоставить итератор для доступа к элементам коллекции, а не передать коллекцию сам.

Я думаю, у вас уже есть.

1 голос
/ 20 апреля 2009

Для случая добавления я бы сказал, инициализируйте его в конструкторе, а что касается непреднамеренного непосредственного экспонирования коллекции, вы можете рассмотреть:

public Collection<Apple> getApples() {
    return Collections.unmodifiableCollection(apples);
}

чтобы люди не использовали геттер, чтобы делать больше, чем получать.

1 голос
/ 20 апреля 2009

Вы можете преобразовать инициализацию в метод и вызвать его из add / delete / update

Это то, что я бы сделал, но я бы сделал метод приватным / защищенным

protected Collection<Apple> getApples() {
  if (apples == null) {
    apples = new LinkedList<Apple>();
  }
  return apples;
}
0 голосов
/ 20 апреля 2009

Поместите это в синхронизированный блок или сделайте его поточно-ориентированным через какой-либо другой механизм (например, CompareAndSet, параллельный контейнер и т. Д.):

if (this.apples == null) {
    apples = new LinkedList<Apple>();
}

потому что в таком виде вы будете забивать яблоки, если несколько потоков попадут внутрь этого блока if одновременно.

0 голосов
/ 20 апреля 2009

Я бы сохранил ленивую инициализацию в сеттере. Получатель может просто вернуть пустую коллекцию, если резервная коллекция равна нулю.

public Collection<Apple> getApples() {
    return apples==null ? Collections.emptyList() : apples;
}

Таким образом, резервная коллекция никогда не создается, если метод вызова вызывается без вызова метода установки.

0 голосов
/ 20 апреля 2009

Сначала вы должны решить, стоит ли накладных расходов на вызов функции. Я полагаю, что вы беспокоитесь не о необработанной скорости, а об объеме памяти.

Сначала я бы добавил это. Обратите внимание, что это личное для класса

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);
}

И все остальное, что использует коллекцию для вашего класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...