Collections.emptyMap () против new HashMap ()

147

В каких ситуациях я могу использовать Collections.emptyMap()? В документации сказано, что я могу использовать этот метод, если хочу, чтобы моя коллекция была неизменной.

Зачем мне неизменяемая пустая коллекция? В чем суть?

Винот Кумар CM
источник
12
+1 к вопросу, потому что я никогда раньше не видел этот статический метод
Тим Бендер
Вы также можете взглянуть на линзы scala, линзы Google Scala . EmptyMap и immutableMap можно использовать для создания неизменяемых объектов. emptyMap - это начальная точка. Каждый раз, когда добавляется элемент, сама карта заменяется картой, содержащей старый элемент и новые элементы. Дело в том, что доступ к объекту безопасен.
michael_s
1
Это похоже на Objective-C [NSArray array], он возвращает объект, который хотя и не может использоваться, но существует. Таким образом, вы можете играть с ним, как с обычным объектом, и не получить ошибку.
Шейн Сюй

Ответы:

146

Из Effective Java , элемент № 43 - "Return empty arrays or collections, not null"демонстрирует возврат пустой коллекции и, возможно, даже демонстрирует использование этих emptyList(), emptySet()и emptyMap()методов в классе Collections для получения пустой коллекции, которая также имеет дополнительное преимущество неизменности. Из пункта №15 "Minimize Mutability" .

Из коллекций-emptySet-Collections-emptyList-Collections

Это тип идиомы программирования. Это для людей, которым не нужны нулевые переменные. Поэтому до инициализации набора они могут использовать пустой набор.

Примечание. Ниже приведен пример кода (измените его в соответствии с вашим вариантом использования):

private Set myset = Collections.emptySet();

void initSet() {
   myset = new HashSet();
}
void deleteSet() {
   myset = Collections.emptySet();
}

Эти методы предлагают несколько преимуществ:

  1. Они более краткие, потому что вам не нужно явно вводить общий тип коллекции - он обычно просто выводится из контекста вызова метода.

  2. Они более эффективны, потому что не создают новых объектов; они просто повторно используют существующий пустой и неизменный объект. Этот эффект обычно очень незначителен, но иногда (ну, редко) важен.

Сумит Сингх
источник
15
+1 за справочник по эффективной Java. Одна нюанс: я бы рекомендовал параметризацию Setи HashSetв ваших примерах, поскольку весь смысл emptySet()метода и его друзей (в отличие от констант Collections.EMPTY_SETи т. Д.) Заключается в том, что они хорошо работают с дженериками. Кроме того, использование функции (необработанные типы), которая устарела с Java 5, не является хорошим учебным пособием.
Дэниел Прайден,
4
Я по-прежнему не убежден ... Разве не весь смысл в использовании a Collectionвместо того, nullчтобы не Exceptionsбыть брошенным на следующие операции? Я полагаю, что использование неизменяемой коллекции приведет к какому-то другому исключению. И присвоение nullопределенно не менее эффективно, чем присвоение неизменной константы.
fgysin восстановит Монику на работе
14
@fgysin: если у вас есть API, в котором ожидается, что клиенты будут изменять коллекцию, тогда да, нет смысла возвращать неизменяемую коллекцию. Но если у вас есть API, в котором вы возвращаете коллекцию, которую клиенты не должны изменять и должны просто выполнять итерацию, тогда возврат представления в базовую коллекцию имеет смысл, чтобы гарантировать, что «плохой» клиент случайно не изменит принадлежащие коллекции тобой. Возврат пустой коллекции вместо null означает, что вашему клиенту не нужно выполнять нулевую проверку перед использованием коллекции, что делает клиентский код более понятным.
Jean Hominal
4
public boolean setExists() { return !myset.equals(Collections.emptySet()); }
assylias
5
В вашем примере вы явно проверяете пустой набор и используете его в качестве контрольного значения, а ваш класс является изменяемым. В вашем небольшом примере используются часовые и изменчивость, что как раз и пытается предотвратить Collections.emptySet ().
Marc O'Morain
33

По моему личному опыту, это очень полезно в тех случаях, когда API требует набора параметров, а вам нечего предоставить. Например, у вас может быть API, который выглядит примерно так и не допускает пустых ссылок:

public ResultSet executeQuery(String query, Map<String, Object> queryParameters);

Если у вас есть запрос, который не принимает никаких параметров, безусловно, немного расточительно создавать HashMap, который включает выделение массива, когда вы можете просто передать `` Пустую карту '', которая фактически является константой, как это реализовано в java.util.Collections.

Аффе
источник
22

Зачем мне неизменяемая пустая коллекция? В чем суть?

Здесь есть две разные концепции, которые кажутся странными, если рассматривать их вместе. Будет больше смысла, если рассматривать эти две концепции по отдельности.

  • Во-первых, вы должны по возможности использовать неизменяемую коллекцию, а не изменяемую. Преимущества иммунитета хорошо описаны в других источниках .

  • Во-вторых, вы должны предпочесть использовать пустую коллекцию, а не использовать null в качестве дозорного. Здесь это хорошо описано . Это означает, что у вас будет гораздо более чистый и понятный код с меньшим количеством мест, где можно спрятать ошибки.

Поэтому, когда у вас есть код, для которого требуется карта, лучше передать пустую карту, а не ноль, чтобы указать на отсутствие карты. И в большинстве случаев, когда вы используете карту, лучше использовать неизменяемую карту. Вот почему есть вспомогательная функция для создания неизменяемой пустой карты.

Марк О'Морайн
источник
8

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

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

В этом случае я предпочитаю возвращать неизменяемые их версии, поскольку это гораздо более четко отражает фактическую неизменность набора результатов вычисления - независимо от того, что вы делаете с данными позже, набор результатов, полученных вами из вашего запроса, не должен изменение.

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

Я думаю об этом как о соглашении о передаче по значению .

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

В качестве очень полезного побочного эффекта это повышенная безопасность и безопасность потоков ваших модулей / служб и обеспечение более четкого разделения проблем.

Еще одна веская причина использовать Collections.empty*()методы - их заметная неполнота. В эпоху до Java7, если у вас была общая коллекция, вам приходилось разбрасывать аннотации универсального типа повсюду.

Просто сравните эти два объявления:

Map<Foo, Comparable<? extends Bar>> fooBarMap = new HashMap<Foo, Comparable<? extends Bar>>();

против:

Map<Foo, Comparable<? extends Bar>> fooBarMap = Collections.emptyMap();

Последний явно выигрывает в удобочитаемости двумя важными способами:

  1. В первом объявлении все создание пустой карты утопает в шуме объявлений универсальных типов, что делает по существу тривиальное объявление гораздо более загадочным, чем должно быть.
  2. В дополнение к заметному отсутствию аннотации универсального типа с правой стороны, во второй версии четко указано, что карта инициализируется пустой картой. Кроме того, зная, что этот метод возвращает неизменяемую карту, теперь мне легче найти, где fooBarMapприсваивается другое непустое значение, просто выполнив поиск /fooBarMap =/.
Роланд Тепп
источник
5

Во-первых, вы можете обойтись без ссылки. Для и new HashMap()т. Д. Потребуется выделенный объект и, возможно, некоторые дополнительные элементы для хранения данных, но вам понадобится только одна копия неизменной пустой коллекции (список, набор, карта или любой другой такой). Это делает очевидным выбор, когда вызываемый вами метод должен принимать карту, но не требует ее редактирования.

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

Джефф Боуман
источник
3

Это может быть полезно, когда у вас есть функция, которая возвращает, immutable collectionи в некоторых ситуациях нет данных для возврата, поэтому вместо возврата nullвы можете вернутьemptyMap()

Это упростит ваш код и предотвратит NullPointerException

iTech
источник
3

В большинстве случаев мы используем a constructorдля создания нового empty map. Но Collections methodsпредложение несколько преимуществ создать с empty mapпомощьюstatic method java.util.Collections.emptyMap()

  1. Они более краткие, потому что вам не нужно явно вводить общий тип коллекции - он обычно просто выводится из контекста вызова метода.

  2. Они более эффективны, потому что не создают новых объектов; они просто повторно используют существующий пустой и неизменный объект. Этот эффект обычно очень незначителен, но иногда (ну, редко) важен.

субодх
источник
2

Зачем мне неизменяемая пустая коллекция? В чем суть?

По той же причине, по которой вы когда- Collections.unmodifiableMap()нибудь использовали . Вы хотите вернуть экземпляр Map, который выдает исключение, если пользователь пытается его изменить. Это просто особый случай: пустая карта.

Райан Стюарт
источник
1

Зачем мне неизменяемая пустая коллекция? В чем суть?

По тем же причинам, почему вам могут понадобиться неизменяемые объекты. В первую очередь потому, что вы можете спокойно спать по ночам, зная, что несколько потоков могут получить доступ к одному и тому же экземпляру объекта и что все они будут видеть одни и те же значения. Отсутствие элементов в коллекции по-прежнему является допустимым значением, которое вы хотели бы сохранить.

vegemite4me
источник