Pytanie Inicjalizacja tablicy ArrayList w jednym wierszu


Chcę utworzyć listę opcji do celów testowych. Na początku zrobiłem to:

ArrayList<String> places = new ArrayList<String>();
places.add("Buenos Aires");
places.add("Córdoba");
places.add("La Plata");

Następnie refaktoryzowałem kod w następujący sposób:

ArrayList<String> places = new ArrayList<String>(
    Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));

Czy jest lepszy sposób to zrobić?


2164
2018-06-17 04:10


pochodzenie


Jeśli jest to przeznaczone do testów jednostkowych, spróbuj wyluzować. Możesz napisać swój kod testowy podczas testowania kodu java i użyć ArrasyList<String> places = ["Buenos Aires", "Córdoba", "La Plata"] - ripper234
W języku Java SE 7 można podstawić sparametryzowany typ konstruktora pustym zestawem parametrów typu (<>): Map <String, List <String> myMap = new HashMap <> (); - Rose
Zobacz też stackoverflow.com/questions/157944/create-arraylist-from-array - Christoffer Hammarström
użyj inicjacji podwójnego usztywniania :) - Mohammad Reza Khatami
Stream.of ("val1", "val2"). Collect (Collectors.toList ()); // tworzy ArrayList, rozwiązanie Java8. - torina


Odpowiedzi:


Właściwie prawdopodobnie "najlepszy" sposób na zainicjowanie ArrayList to metoda, którą napisałeś, ponieważ nie musisz tworzyć nowego List w dowolny sposób:

ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");

Połów jest taki, że wymaga się pisania na klawiaturze list instancja.

Istnieją alternatywy, takie jak tworzenie anonimowej klasy wewnętrznej za pomocą inicjalizatora instancji (znanego również jako "inicjalizacja podwójnego klamra"):

ArrayList<String> list = new ArrayList<String>() {{
    add("A");
    add("B");
    add("C");
}};

Jednak nie przepadam za tą metodą, ponieważ to, co otrzymujesz, jest podklasą ArrayList który ma inicjator instancji i ta klasa jest tworzona tylko po to, aby stworzyć jeden obiekt - to wydaje się trochę przesadą dla mnie.

Co by było fajnie było, gdyby Kolekcja Literały propozycja dla Moneta projektu został zaakceptowany (miał być wprowadzony w Javie 7, ale prawdopodobnie nie jest częścią Java 8):

List<String> list = ["A", "B", "C"];

Niestety to ci nie pomoże, ponieważ zainicjuje niezmienną List raczej niż ArrayLista ponadto nie jest jeszcze dostępna, jeśli kiedykolwiek będzie.


1627
2018-06-17 04:13



Widzieć stackoverflow.com/questions/924285 aby uzyskać więcej informacji na temat inicjalizacji podwójnego klamra, plusy i minusy. - Eddie
@Eddie: Dobre wołanie o link - jedno zdanie na temat inicjacji podwójnymi nawiasami nie wystarcza do pełnego opisania. - coobird
Działa tylko wtedy, gdy nie polegasz na Auto boxing List <Double> list = [1.0, 2.0, 3.0]; zawiedzie. - Richard B
Cóż, po prostu się okazuje - syntaktyczny cukier jednego człowieka to kolejna męska cukrzyca składniowa. Będę trzymać się z daleka od tych "alternatyw". - corsiKa
Ponieważ ta odpowiedź jest najbardziej uprzywilejowaną i wspomnianą Monetą Projektu, myślę, że powinieneś zawołać, że java 9 jest dostarczana ze składnią List.of (...): docs.oracle.com/javase/9/docs/api/java/util/List.html#of-E...- - Asaf


Byłoby prostsze, gdybyś po prostu zadeklarował to jako List - czy musi to być ArrayList?

List<String> places = Arrays.asList("Buenos Aires", "Córdoba", "La Plata");

Lub jeśli masz tylko jeden element:

List<String> places = Collections.singletonList("Buenos Aires");

To by znaczyło, że places jest niezmienny (próba zmiany spowoduje powstanie UnsupportedOperationException wyjątek do rzucenia).

Aby utworzyć zmienną listę, która jest konkretna ArrayList możesz stworzyć ArrayList z niezmiennej listy:

ArrayList<String> places = new ArrayList<>(Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));

1734
2018-06-17 04:15



@Marcase: Czy nie możesz zmienić swojej klasy, aby używać Listy zamiast ArrayList? - Lawrence Dol
Zgodnie z moją odpowiedzią, jeśli nie używasz konkretnych metod ArrayList, byłoby lepiej zaprojektować zmianę deklaracji na List. Określ interfejsy, a nie implementacje. - Christoffer Hammarström
@ Christoffer Hammarström: jeśli zmienia deklarację na Lista i używa Lista <String> places = Arrays.asList (...); nie będzie mógł go użyć places.add ("blabla") - maks
Gwoli ścisłości, asList(...) zwraca stały rozmiar List który wysadza w powietrze takie mutacyjne operacje jak remove i clear, rzeczy List roszczenia kontraktowe do wsparcia. Nawet jeśli zostawiłeś deklarację jako List, musisz użyć List l = new ArrayList(asList(...)) w celu uzyskania obiektu, który nie wyrzuci wyjątków OperationNotSupported. Liskov Substitution Zasada ktoś? - Splash
@Pluśnięcie: remove i clear są opcjonalne operacje w List, więc asList(...) postępuje zgodnie z umową. Nigdzie PO nie twierdzi, że potrzebuje dodać więcej elementów później, przykład jest po prostu List to musi być zainicjalizowane trzema elementami. - Christoffer Hammarström


Prosta odpowiedź

W języku Java 8 lub wcześniejszym:

List<String> strings = Arrays.asList("foo", "bar", "baz");

To da ci List wspierane przez tablicę, więc nie może zmienić długości.
Ale możesz zadzwonić List.set, więc wciąż jest zmienna.


W języku Java 9:

List<String> strings = List.of("foo", "bar", "baz");

To da ci niezmienny List, więc nie można go zmienić.
Właśnie tego chcesz w większości przypadków, gdy je przygotujesz.


Krótsza odpowiedź

Możesz zrobić Arrays.asList jeszcze krótszy ze statycznym importem:

List<String> strings = asList("foo", "bar", "baz");

Statyczny import:

import static java.util.Arrays.asList;  

Które nowoczesne IDE zasugeruje i automatycznie zrobi dla ciebie.
Na przykład w IntelliJ IDEA naciskasz Alt+Enter i wybierz Static import method....


Jednak nie zalecam skracania środowiska Java 9 List.of metoda, ponieważ mająca właśnie of staje się mylące.
List.of jest już wystarczająco krótki i dobrze czyta.


Za pomocą Streams

Dlaczego to musi być List?
W Java 8 lub nowszym możesz użyć Stream który jest bardziej elastyczny:

Stream<String> strings = Stream.of("foo", "bar", "baz");

Możesz się łączyć Streams:

Stream<String> strings = Stream.concat(Stream.of("foo", "bar"),
                                       Stream.of("baz", "qux"));

Lub możesz przejść od Stream do a List:

List<String> strings = Stream.of("foo", "bar", "baz").collect(toList());

Ale najlepiej, po prostu użyj Stream bez zbierania go do List.


Jeśli ty naprawdę konkretnie potrzebujesz java.util.ArrayList

(Prawdopodobnie nie.)
Cytować JEP 269 (podkreślenie moje):

Tam jest mały zestaw przypadków użycia w celu zainicjowania zmiennej instancji kolekcji przy użyciu predefiniowanego zestawu wartości. Zazwyczaj lepiej jest, gdy te wstępnie zdefiniowane wartości znajdują się w niezmiennej kolekcji, a następnie, aby zainicjować kolekcję zmiennych za pomocą konstruktora kopiowania.


Jeśli chcesz obie prepropulate an ArrayList  i dodaj do tego później (dlaczego?), użyj

List<String> strings = new ArrayList<>(asList("foo", "bar", "baz"));

lub w Javie 9:

List<String> strings = new ArrayList<>(List.of("foo", "bar", "baz"));

lub za pomocą Stream:

List<String> strings = Stream.of("foo", "bar", "baz")
                             .collect(toCollection(ArrayList::new));

Ale znowu, lepiej po prostu użyć Stream bezpośrednio zamiast zbierać go do List.


Programuj interfejsy, a nie implementacje

Powiedziałeś, że zadeklarowałeś listę jako ArrayList w kodzie, ale powinieneś to zrobić tylko, jeśli używasz jakiegoś członka ArrayList tego nie ma List.

Które najprawdopodobniej nie robisz.

Zazwyczaj wystarczy zadeklarować zmienne za pomocą najbardziej ogólnego interfejsu, którego zamierzasz użyć (np. Iterable, Collection, lub List) i zainicjować je za pomocą konkretnej implementacji (np. ArrayList, LinkedList lub Arrays.asList()).

W przeciwnym razie ograniczasz swój kod do tego konkretnego typu, a będzie trudniej go zmienić, kiedy chcesz.

Na przykład:

// Iterable if you just need iteration, for (String s : strings):
Iterable<String> strings = new ArrayList<>();   

// Collection if you also need .size() or .stream():
Collection<String> strings = new ArrayList<>(); 

// List if you also need .get(index):
List<String> strings = new ArrayList<>();       

// Don't declare a specific list implementation
// unless you're sure you need it:
ArrayList<String> strings = new ArrayList<>();  // You don't need ArrayList

Innym przykładem może być zawsze deklarowanie zmiennej an InputStream mimo że zazwyczaj jest to FileInputStream lub BufferedInputStream, ponieważ pewnego dnia ty lub ktoś inny będzie chciał użyć innego rodzaju InputStream.


582
2017-09-09 12:33



Bardzo dziękuję. Próbowałem pomijać "statyczny" import w moim kodzie, ale kompilator nie miałby tego. Dlaczego statyczne jest niezbędne do działania kodu? - jollyroger
@Jolly Roger: Arrays.asList jest metodą statyczną. Widzieć docs.oracle.com/javase/1.5.0/docs/guide/language/... - Christoffer Hammarström
@ ChristofferHammarström W tym przypadku mój nowicjusz powiedział mi, że import statyczny wykazuje poważne podobieństwo do zmiennych globalnych i zagrożeń związanych z takim użyciem. Czy to założenie jest poprawne i czy jest to również powód, dla którego podobna odpowiedź powyżej daje więcej głosów? - jollyroger
Jeśli nie chcesz statycznego importu, możesz również zdefiniować pełną ścieżkę do statycznej metody asList, tak jak to: List <String> string = java.util.Arrays.asList ("", ""); - Geert Weening
Lub możesz użyć import java.util.Arrays; i Arrays.asList ("", ""); Nie musisz używać statycznego importu. Nie musisz używać pełnej ścieżki. Metody statyczne nie dbają o import. Po prostu denerwują się, jeśli użyjesz instancji, aby je znaleźć. - candied_orange


Jeśli potrzebujesz prostej listy o rozmiarze 1:

List<String> strings = new ArrayList<String>(Collections.singletonList("A"));

Jeśli potrzebujesz listy kilku obiektów:

List<String> strings = new ArrayList<String>();
Collections.addAll(strings,"A","B","C","D");

100
2017-08-30 04:33





Z Guava Możesz pisać:

ArrayList<String> places = Lists.newArrayList("Buenos Aires", "Córdoba", "La Plata");

W Guava są też inne użyteczne konstruktory statyczne. Możesz o nich przeczytać tutaj.


52
2017-07-29 13:24



Jestem prawie pewien, że możesz to zrobić właśnie java.util.Arrays Jak na przykład, List<String> names = Arrays.asList("Beckah", "Sam", "Michael"); - beckah
@beckah method Arrays.asLists tworzy obiekt typu List, natomiast pytanie dotyczy tworzenia ArrayList - Paweł Adamski
Ta metoda nie jest zbyt użyteczna i prawdopodobnie zostanie wycofana w przyszłości. - shmosel


Literały kolekcji nie pojawiły się w Javie 8, ale możliwe jest użycie Stream API do zainicjowania listy w jednym raczej długim wierszu:

List<String> places = Stream.of("Buenos Aires", "Córdoba", "La Plata").collect(Collectors.toList());

Jeśli chcesz zapewnić, że twoje List to jest ArrayList:

ArrayList<String> places = Stream.of("Buenos Aires", "Córdoba", "La Plata").collect(Collectors.toCollection(ArrayList::new));

32
2018-04-03 23:21





import com.google.common.collect.ImmutableList;

....

List<String> places = ImmutableList.of("Buenos Aires", "Córdoba", "La Plata");

26
2018-05-12 13:14



Nie chcę dodawać nowej zależności, aby to zrobić. - Macarse
To jest to samo Collections.unmodifiableList(Arrays.asList("Buenos Aires", "Córdoba", "La Plata")), która staje się unmodifiableList(asList("Buenos Aires", "Córdoba", "La Plata")) z importem statycznym. Nie potrzebujesz do tego Google Collections. - Christoffer Hammarström
Nie, to nie to samo. Jak ImmutableList dokumentuje jego niezmienność w typie wyniku, gdy niemodyfikowalna LISTA maskuje go jako normalną Listę. - David Pierre
Zamiast niezmiennego kolekcje google oferują także zmienną listę tablic: List <String> = Lists.newArrayList ("Buenos Aires", "Córdoba", "La Plata"); - L. Holanda
Mij ​​to ImmutableList do innych metod, które wymagają List, a potem i tak straciłeś tę dokumentację. - Christoffer Hammarström


Możesz stworzyć metodę fabryczną:

public static ArrayList<String> createArrayList(String ... elements) {
  ArrayList<String> list = new ArrayList<String>();
  for (String element : elements) {
    list.add(element);
  }
  return list;
}

....

ArrayList<String> places = createArrayList(
  "São Paulo", "Rio de Janeiro", "Brasília");

Ale nie jest to dużo lepsze od twojego pierwszego refaktoryzacji.

Dla większej elastyczności może być generyczny:

public static <T> ArrayList<T> createArrayList(T ... elements) {
  ArrayList<T> list = new ArrayList<T>();
  for (T element : elements) {
    list.add(element);
  }
  return list;
}

22
2018-05-04 00:57



Wróć do pierwotnego postu, prosi o inicjalizację macierzy w jedna linia, a nie 7 dodatkowych linii. - L. Holanda
@LeoHolanda: Tworzenie fabrycznych metod dla każdej małej rzeczy to za dużo, zgadzam się. Ale w zależności na temat sytuacji i tego, ile razy ta metoda zostanie użyta, może być sens jej utworzenie. Tworzenie dodatkowych warstw abstrakcji ma na celu usunąć złożoność, tworząc więcej znaczący metody przechwytujące zamiar projektanta. - Jordão


Z Java 9, jak zasugerowano w Propozycja ulepszenia JDK - 269, można to osiągnąć za pomocą literały dotyczące kolekcji teraz jako -

List<String> list = List.of("A", "B", "C");

Set<String> set = Set.of("A", "B", "C");

Podobne podejście miałoby zastosowanie Map także -

Map<String, String> map = Map.of("k1", "v1", "k2", "v2", "k3", "v3")

który jest podobny do Kolekcja Literały propozycja jak również stwierdził @coobird. Ponadto wyjaśniono to w dokumencie JEP -


Alternatywy

Zmiany językowe były kilkakrotnie rozważane i odrzucane:

Propozycja monety projektowej, 29 marca 2009 r 

Propozycja monety projektowej, 30 marca 2009 r 

JEP 186 dyskusja na temat lambda-dev, styczeń-marzec 2014

Język   wnioski zostały odrzucone zamiast propozycji opartej na bibliotece jako   podsumowane w tym wiadomość.

Przykuty czytać o tym samym ~> Jaki jest sens przeciążenia Fabrycznych Metod Convenience


17
2018-02-02 17:36