Collections.emptyList () против нового экземпляра

241

На практике лучше вернуть пустой список примерно так :

return Collections.emptyList();

Или вот так :

return new ArrayList<Foo>();

Или это полностью зависит от того, что вы собираетесь делать с возвращенным списком?

MRE
источник

Ответы:

300

Основное отличие состоит в том, что Collections.emptyList()возвращает неизменный список, т. Е. Список, в который нельзя добавлять элементы. (То же относится и к List.of()введенному в Java 9.)

В тех редких случаях , когда вы действительно хотите изменить возвращаемый список, Collections.emptyList()и List.of(), таким образом , не хороший выбор.

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


Кроме того, emptyList() может не создавать новый объект с каждым вызовом.

Реализации этого метода не должны создавать отдельный объект List для каждого вызова. Использование этого метода может иметь сравнимую стоимость с использованием одноименного поля. (В отличие от этого метода, поле не обеспечивает безопасность типов.)

Реализация emptyListвыглядит следующим образом:

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

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

aioobe
источник
4
Итак, Collections.emptyList()подойдет ли, скажем так, проверка ошибок и тому подобное?
MRE
1
Клиенты API не получат NullPointerException, вернув Collections.emptyList()вместо null.
realPK
@PK_J делает важный момент. Collections.emptyList()является итеративным и возвращает длину, поэтому его можно использовать в циклах for без исключения.
ndm13
как насчет использования List.of()?
4
@ AJW, да. Но по сравнению, скажем, new ArrayList<>()это также делает проектное решение ясным; элементы не будут добавлены в этот список.
aioobe
51

Начиная с Java 5.0, вы можете указать тип элемента в контейнере:

Collections.<Foo>emptyList()

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

Пол Джексон
источник
38
Начиная с Java 7, вы можете позволить компилятору выводить параметр типа вызова универсального метода из целевого типа:List<Foo> list = Collections.emptyList()
Пол Джексон
28

Collections.emptyList является неизменным, поэтому существует разница между двумя версиями, поэтому вы должны учитывать пользователей возвращаемого значения.

Возврат new ArrayList<Foo>всегда создает новый экземпляр объекта, поэтому с ним связаны очень небольшие дополнительные расходы, которые могут дать вам повод для использования Collections.emptyList. Мне нравится использовать emptyListтолько потому, что это более читабельно.

Джефф Фостер
источник
14

Будьте осторожны, хотя. Если вы вернетесь, Collections.emptyList()а затем попытаетесь внести в него некоторые изменения, например add()или что-то в этом роде, у вас будет UnsupportedOperationException()потому что Collections.emptyList()возвращает неизменный объект.

Сергей Фролов
источник
7

Я хотел бы пойти с, Collections.emptyList()если возвращенный список не изменяется каким-либо образом (так как список является неизменным), в противном случае я бы пошел с вариантом 2.

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

S73417H
источник
3

Используйте Collections.emptyList (), если вы хотите убедиться, что возвращаемый список никогда не изменяется. Вот что возвращается при вызове emptyList ():

/**
 * The empty list (immutable). 
 */
public static final List EMPTY_LIST = new EmptyList();
Атула
источник
Я прибыл сюда, пытаясь выяснить, была ли стоимость звонка Collections.emptyList()строительства. Просмотр подробностей реализации (хотя, вероятно, не одинаковых на всех JVM) подтверждает, что это не так. @ Атул, из какой это JVM?
13
2

Приведенные ответы подчеркивают тот факт, что emptyList()возвращает неизменное, Listно не дают альтернативы. Конструктору ArrayList(int initialCapacity)особые случаи 0так возвращающиеся new ArrayList<>(0)вместо new ArrayList<>()также может быть жизнеспособным решением:

/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

[...]

/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

(исходники из Java 1.8.0_72)

Рене
источник
Я не согласен с вашим подходом. Вы экономите немного памяти и ЦП при инициализации, но если список, который вы вернули, когда-либо изменяется, вы теряете то время, когда список перераспределяет новый массив. Если со временем в список будет добавлено много элементов, это может привести к увеличению узкого места в производительности из- за гораздо более медленного роста . Я предпочитаю придерживаться соглашения о неизменяемом пустом списке или пригодном для использования, изменяемом списке.
Патрик М
1
Как я пытался подчеркнуть с моей формулировкой ( может быть жизнеспособным ): все зависит от вашего варианта использования. Я , как правило , либо возвращать изменяемую или unmutable Коллекции, не смесь в зависимости от кастрированного барана они пустые или нет. И противостоять «гораздо более медленным претензиям»: это текущая реализация.
Рене
О, чувак, посмотри на меня, ссылаясь на то, что JDK 2 основных версии устарели. Таким образом, java8 полностью устраняет узкое место, перепрыгивая на емкость по умолчанию с начального размера 0. Извините, я был так неправ.
Патрик М