Collections.emptyList () vs. nowe wystąpienie

241

W praktyce, czy lepiej jest zwrócić pustą listę w następujący sposób :

return Collections.emptyList();

Albo jak to :

return new ArrayList<Foo>();

Czy może to całkowicie zależy od tego, co zrobisz ze zwróconą listą?

mre
źródło

Odpowiedzi:

300

Główną różnicą jest to, że Collections.emptyList()zwraca niezmienną listę, tj. Listę, do której nie można dodawać elementów. (To samo dotyczy List.of()wprowadzonych w Javie 9.)

W rzadkich przypadkach, gdy chcesz zmodyfikować zwróconą listę, Collections.emptyList()a List.of()zatem nie są dobrym wyborem.

Powiedziałbym, że zwrócenie niezmiennej listy jest całkowicie w porządku (a nawet preferowanym sposobem), o ile umowa (dokumentacja) wyraźnie nie określa inaczej.


Ponadto emptyList() może nie utworzyć nowego obiektu przy każdym wywołaniu.

Implementacje tej metody nie muszą tworzyć osobnego obiektu List dla każdego wywołania. Korzystanie z tej metody prawdopodobnie będzie miało koszt porównywalny z użyciem pola o podobnej nazwie. (W przeciwieństwie do tej metody pole nie zapewnia bezpieczeństwa typu).

Implementacja emptyListwygląda następująco:

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

Jeśli więc twoja metoda (która zwraca pustą listę) jest wywoływana bardzo często, to podejście może nawet dać ci nieco lepszą wydajność zarówno pod względem procesora, jak i pamięci.

aioobe
źródło
4
Czy byłoby Collections.emptyList()bardziej odpowiednie, powiedzmy, sprawdzanie błędów i tym podobne?
MRE
1
Klienci interfejsu API nie otrzymają NullPointerExceptionzwrotu, Collections.emptyList()zamiast null.
realPK
@PK_J robi ważny punkt. Collections.emptyList()jest iterowalny i zwraca długość, dzięki czemu można go używać w pętlach bez zgłaszania wyjątku.
ndm13
co z użyciem List.of()?
4
@AJW, tak. Ale w porównaniu do, powiedzmy, new ArrayList<>()wyraźnie wyjaśnia decyzję projektową; elementy nie zostaną dodane do tej listy.
aioobe
51

Począwszy od Java 5.0 możesz określić typ elementu w kontenerze:

Collections.<Foo>emptyList()

Zgadzam się z innymi odpowiedziami, że w przypadkach, w których chcesz zwrócić pustą listę, która pozostaje pusta, powinieneś zastosować to podejście.

Paul Jackson
źródło
38
Począwszy od Java 7, możesz pozwolić kompilatorowi wyprowadzić parametr typu wywołania metody ogólnej z typu docelowego:List<Foo> list = Collections.emptyList()
Paul Jackson
28

Collections.emptyList jest niezmienny, więc istnieje różnica między dwiema wersjami, więc musisz wziąć pod uwagę użytkowników zwracanej wartości.

Zwracanie new ArrayList<Foo>zawsze tworzy nową instancję obiektu, więc wiąże się z nią bardzo niewielki dodatkowy koszt, który może być powodem do użycia Collections.emptyList. Lubię używać emptyListtylko dlatego, że jest bardziej czytelny.

Jeff Foster
źródło
14

Bądź jednak ostrożny. Jeśli wrócisz, Collections.emptyList()a następnie spróbujesz wprowadzić pewne zmiany, takie jak add()lub coś w tym stylu, będziesz mieć UnsupportedOperationException()bo, ponieważ Collections.emptyList()zwraca niezmienny obiekt.

Siergiej Frolow
źródło
7

Poszedłbym z Collections.emptyList() jeśli zwrócona lista nie jest w żaden sposób modyfikowana (ponieważ lista jest niezmienna), w przeciwnym razie wybrałbym opcję 2.

Zaletą Collections.emptyList()jest to, że za każdym razem zwracana jest ta sama instancja statyczna, dlatego nie ma tworzenia instancji dla każdego wywołania.

S73417H
źródło
3

Użyj Collections.emptyList (), jeśli chcesz mieć pewność, że zwrócona lista nigdy nie zostanie zmodyfikowana. Oto, co jest zwracane po wywołaniu emptyList ():

/**
 * The empty list (immutable). 
 */
public static final List EMPTY_LIST = new EmptyList();
Atul
źródło
Przybyłem tutaj, próbując dowiedzieć się, czy połączenie Collections.emptyList()ma koszt budowy. Wyświetlanie szczegółów implementacji (choć prawdopodobnie nie jest takie samo na wszystkich maszynach JVM) potwierdza, że ​​tak nie jest. @Atul, z której to JVM?
wjl
2

Podane odpowiedzi podkreślają fakt, że emptyList()zwraca niezmienne, Listale nie dają alternatyw. ArrayList(int initialCapacity)Przypadki specjalne Konstruktora, 0więc powrót new ArrayList<>(0)zamiast new ArrayList<>()może być również realnym rozwiązaniem:

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

(źródła z Java 1.8.0_72)

René
źródło
Nie zgadzam się z twoim podejściem. Podczas inicjalizacji oszczędzasz trochę pamięci i procesora, ale jeśli zwrócona lista zostanie kiedykolwiek zmodyfikowana, stracisz ten czas, gdy lista ponownie przydzieli nową tablicę. Jeśli z czasem na liście pojawi się wiele elementów, może to narastać w wąskim gardle wydajności ze względu na znacznie wolniejsze tempo wzrostu . Wolę trzymać się konwencji niemodyfikowalnej pustej listy lub użytecznej, modyfikowalnej listy.
Patrick M
1
Próbowałem podkreślić moje sformułowanie ( może być opłacalne) ): wszystko zależy od twojego przypadku użycia. I na ogół albo powrócić zmienny lub unmutable zbiorów, a nie mieszaniny w zależności od wether są puste czy nie. I przeciwdziałać „znacznie wolniejszy roszczenie”: to jest obecna implementacja.
René
O rany, spójrz na mnie, powołując się na nieaktualne główne wersje JDK 2. Tak więc java8 całkowicie eliminuje wąskie gardło, skacząc do domyślnej pojemności z początkowego rozmiaru 0. Przepraszam, że tak się myliłem.
Patrick M