Lista typów vs Typ ArrayList w Javie [duplikat]
(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
Rozumiem, że za pomocą (1) można zamienić implementacje interfejsu List. Wygląda na to, że (1) jest zazwyczaj używany w aplikacji niezależnie od potrzeby (sam zawsze tego używam).
Zastanawiam się, czy ktoś używa (2)?
Również, jak często (i czy mogę podać przykład) sytuacja wymaga użycia (1) over (2) (tzn. gdzie (2) nie wystarczyłoby..na bok kodowanie do interfejsy oraz najlepsze praktyki itd.)
15 answers
Prawie zawsze List
jest preferowane od ArrayList
, ponieważ na przykład List
można przetłumaczyć na LinkedList
bez wpływu na resztę kodu.
Jeśli użyto ArrayList
zamiast List
, trudno jest zmienić implementację ArrayList
na LinkedList
, ponieważ ArrayList
w bazie kodowej zastosowano określone metody, które również wymagałyby restrukturyzacji.
Możesz przeczytać o List
implementacjach tutaj .
Możesz zacząć od ArrayList
, ale wkrótce po odkryciu, że inna implementacja jest bardziej odpowiednim wyborem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-24 10:26:21
Tak. Ale rzadko z rozsądnego powodu (IMO).Zastanawiam się, czy ktoś używa (2)?
A ludzie się palą, bo używali ArrayList
kiedy powinni używać List
:
Metody użytkowe, takie jak
Collections.singletonList(...)
lubArrays.asList(...)
nie zwracająArrayList
.Metody w API
List
nie gwarantują zwracania listy tego samego typu.
Np. ktoś się poparzył, w https://stackoverflow.com/a/1481123/139985 plakat miał problemy z "krojeniem", ponieważ ArrayList.sublist(...)
nie zwraca ArrayList
... i zaprojektował swój kod tak, aby używał ArrayList
jako typu wszystkich zmiennych listy. Ostatecznie" rozwiązał " problem, kopiując sublist do nowego ArrayList
.
Argument, który musisz wiedzieć, jak zachowuje się List
, jest w dużej mierze rozwiązywany za pomocą interfejsu znacznika RandomAccess
. Tak, jest trochę niezgrabny, ale alternatywa jest gorsza.
Ponadto, jak często sytuacja wymaga użycia (1) over (2) (tzn. gdzie (2) nie wystarcza..pomijając "kodowanie do interfejsów" i najlepsze praktyki itp.)
" jak często " część pytania jest obiektywnie bez odpowiedzi.
(i czy Mogę prosić o przykład)
Czasami aplikacja może wymagać użycia metod w API ArrayList
, które są , a nie W API List
. Na przykład, ensureCapacity(int)
, trimToSize()
lub removeRange(int, int)
. (Oraz ta ostatnia pojawi się tylko wtedy, gdy utworzyłeś Podtyp ArrayList, który deklaruje metodę jako public
.)
To jest jedyny powód, dla którego kodowanie do klasy, A nie interfejsu, IMO.
(jest teoretycznie możliwe, że uzyskasz niewielką poprawę wydajności ... w pewnych okolicznościach ... na niektórych peronach ... ale jeśli naprawdę potrzebujesz tego ostatniego 0.05%, nie warto tego robić. To nie jest rozsądny powód, IMO.)
To słuszna Uwaga. Jednak Java zapewnia lepsze sposoby radzenia sobie z tym problemem, np.]}Nie możesz napisać efektywnego kodu, jeśli nie wiesz, czy dostęp losowy jest skuteczny, czy nie.
public <T extends List & RandomAccess> void test(T list) {
// do stuff
}
Jeśli wywołasz to z listą, która nie implementuje RandomAccess
, otrzymasz błąd kompilacji.
instanceof
... jeśli statyczne pisanie jest zbyt niezręczne. I można nawet napisać swój kod, aby korzystać z różnych algorytmów (dynamicznie) w zależności od tego, czy lista obsługuje dostęp losowy.
Zauważ, że ArrayList
nie jest jedyną klasą list implementującą RandomAccess
. Inne to CopyOnWriteList
, Stack
i Vector
.
Widziałem, jak ludzie robią ten sam argument o Serializable
(ponieważ List
tego nie implementuje)... ale powyższe podejście rozwiązuje również ten problem. (W zakresie, w jakim jest to rozwiązywalne w ogóle przy użyciu typów runtime. ArrayList
nie powiedzie się serializacja, jeśli jakikolwiek element nie jest serializowalny.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-06 01:30:52
Na przykład możesz zdecydować, że LinkedList
jest najlepszym wyborem dla Twojej aplikacji, ale później zdecyduj, że ArrayList
może być lepszym wyborem ze względu na wydajność.
Użycie:
List list = new ArrayList(100); // will be better also to set the initial capacity of a collection
Zamiast:
ArrayList list = new ArrayList();
Dla odniesienia:
(posted by Schematy kolekcji)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-09 15:12:05
Jest dobry styl aby zapisać odniesienie do HashSet
LUB TreeSet
w zmiennej typu Set.
Set<String> names = new HashSet<String>();
W ten sposób musisz zmienić tylko jedną linię, jeśli zdecydujesz się użyć TreeSet
.
Również metody operujące na zestawach powinny określać parametry typu Set:
public static void print(Set<String> s)
Then metoda może być stosowana we wszystkich implementacjach zestawu.
Teoretycznie powinniśmy zrobić to samo rekomendacja dla list linkowanych, czyli zapisywanie
LinkedList odwołuje się do zmiennych typu List. Jednak w bibliotece Java interfejs listy jest wspólny zarówno dla klasy ArrayList
, jak i LinkedList
. W szczególności posiada metody get I set dla dostępu losowego, mimo że metody te są bardzo nieefektywne dla list połączonych.
Ty nie można napisać efektywnego kodu Jeśli nie wiesz, czy dostęp losowy jest skuteczny, czy nie.
To jest po prostu poważny projekt błąd w bibliotece standardowej i nie polecam używania interfejs listy z tego powodu.
Aby zobaczyć, jak żenujący jest ten błąd, spójrz na
kod źródłowy metody binarySearch
Kolekcje klasy. Ta metoda zajmuje
Parametr List, ale Wyszukiwanie binarne nie ma sensu dla listy linkowanej. Kod wtedy niezdarny
próbuje sprawdzić, czy lista jest listą połączoną, a następnie przełączy się na wyszukiwanie liniowe!
Interfejs Set
i Map
Interfejs, są dobrze zaprojektowane, i należy z nich korzystać.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-16 08:35:03
Używam (2), jeśli kod jest "właścicielem" listy. Jest to na przykład prawda dla zmiennych tylko lokalnych. Nie ma powodu, aby używać typu abstrakcyjnego List
zamiast ArrayList
.
Inny przykład do wykazania własności:
public class Test {
// This object is the owner of strings, so use the concrete type.
private final ArrayList<String> strings = new ArrayList<>();
// This object uses the argument but doesn't own it, so use abstract type.
public void addStrings(List<String> add) {
strings.addAll(add);
}
// Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list.
public List<String> getStrings() {
return Collections.unmodifiableList(strings);
}
// Here we create a new list and give ownership to the caller. Use concrete type.
public ArrayList<String> getStringsCopy() {
return new ArrayList<>(strings);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-21 12:20:37
Kiedy piszesz List
, w rzeczywistości mówisz, że Twój obiekt implementuje tylko interfejs List
, ale nie określasz, do jakiej klasy należy twój obiekt.
Kiedy piszesz ArrayList
, określasz, że klasa obiektu jest zmienną-array.
Więc pierwsza wersja uczyni Twój kod bardziej elastycznym w przyszłości.
Spójrz na dokumenty Javy:
Klasa ArrayList
- Resizable-array implementacja interfejsu List
.
Interfejs List
- Na zbiór uporządkowany (znany również jako sekwencja). Użytkownik tego interfejsu ma precyzyjną kontrolę nad tym, gdzie na liście jest wstawiany każdy element.
Array
- obiekt kontenera, który przechowuje stałą liczbę wartości jednego typu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-05 17:14:10
Myślę, że ludzie, którzy używają (2) nie znają Zasady substytucji Liskowa ani Zasady inwersji zależności. Albo naprawdę muszą użyć ArrayList
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 07:55:27
W rzeczywistości są sytuacje, w których (2) jest nie tylko preferowane, ale obowiązkowe i jestem bardzo zaskoczony, że nikt o tym tutaj nie wspomina.
Serializacja!Jeśli masz klasę serializowalną i chcesz, aby zawierała listę, musisz zadeklarować pole jako konkretny i serializowalny Typ, taki jak ArrayList
, ponieważ interfejs List
nie rozszerza java.io.Serializable
Oczywiście większość ludzi nie potrzebuje serializacji i zapomina o tym.
Przykład:
public class ExampleData implements java.io.Serializable {
// The following also guarantees that strings is always an ArrayList.
private final ArrayList<String> strings = new ArrayList<>();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-14 11:20:47
(3) Collection myCollection = new ArrayList<?>();
Używam tego zazwyczaj. I tylko jeśli potrzebuję metod List, użyję List. To samo z ArrayList. Zawsze możesz przełączyć się na bardziej "wąski" interfejs, ale nie możesz przełączyć się na bardziej "szeroki".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-20 12:53:32
Z dwóch następujących:
(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
Pierwszy jest ogólnie preferowany. Ponieważ będziesz używać metod tylko z interfejsu List
, daje Ci to swobodę korzystania z innej implementacji List
, np. LinkedList
w przyszłości. Więc oddziela cię od konkretnej implementacji. Teraz warto wspomnieć o dwóch punktach:
- zawsze powinniśmy programować do interfejsu. Więcej tutaj .
- prawie zawsze będziesz używać
ArrayList
przezLinkedList
. Więcej tutaj .
Zastanawiam się czy ktoś używa (2)
Tak Czasami (czytane rzadko). Kiedy potrzebujemy metod, które są częścią implementacji ArrayList
, ale nie częścią interfejsu List
. Na przykład ensureCapacity
.
Również, jak często (i czy mogę dostać przykład) robi sytuacja w rzeczywistości wymaga użycia (1) over (2)
Prawie zawsze preferujesz opcję (1). Jest to klasyczny wzorzec projektowy w OOP, gdzie zawsze starasz się oddzielenie kodu od konkretnej implementacji i programu od interfejsu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:24
Lista jest interfejsem. Nie ma metod. Gdy wywołujesz metodę na liście referencji, w rzeczywistości wywołuje ona metodę ArrayList w obu przypadkach.
I na przyszłość można zmienić List obj = new ArrayList<>
na List obj = new LinkList<>
lub inne typy implementujące List interface .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-20 13:10:59
Ktoś zapytał o to ponownie (duplikat), co sprawiło, że zagłębiłem się nieco w tę kwestię.
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
ArrayList<String> aList = new ArrayList<String>();
aList.add("a");
aList.add("b");
}
Jeśli używamy przeglądarki kodu bajtowego (użyłem http://asm.ow2.org/eclipse/index.html ) widzimy nastÄ ™ pujÄ ... cy (tylko inicjalizacjä ™ listy i przypisanie) fragment naszej listy:
L0
LINENUMBER 9 L0
NEW ArrayList
DUP
INVOKESPECIAL ArrayList.<init> () : void
ASTORE 1
L1
LINENUMBER 10 L1
ALOAD 1: list
LDC "a"
INVOKEINTERFACE List.add (Object) : boolean
POP
L2
LINENUMBER 11 L2
ALOAD 1: list
LDC "b"
INVOKEINTERFACE List.add (Object) : boolean
POP
I dla :
L3
LINENUMBER 13 L3
NEW java/util/ArrayList
DUP
INVOKESPECIAL java/util/ArrayList.<init> ()V
ASTORE 2
L4
LINENUMBER 14 L4
ALOAD 2
LDC "a"
INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
POP
L5
LINENUMBER 15 L5
ALOAD 2
LDC "b"
INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
POP
Różnica polega na tym, że list kończy się wywołaniem INVOKEINTERFACE podczas gdy alist wywołuje INVOKEVIRTUAL . Accoding to bycode Outline plugin reference,
Invokeinterface jest używany do wywołania metody zadeklarowanej w Javie interfejs
While invokevirtual
Wywołuje wszystkie metody z wyjątkiem metod interfejsu (które używają invokeinterface), metody statyczne (które używają invokestatic), oraz kilka sprawy specjalne prowadzone przez invokespecial.
Podsumowując, invokevirtual wyskakuje objectref ze stosu podczas gdy dla invokeinterface
Interpreter wyskakuje' n 'ze stosu operandu, gdzie' n ' jest 8-bitowym niepodpisanym parametr integer pobrany z kodu bajtowego. Pierwsza z tych pozycji to objectref, odniesienie do obiektu, którego metoda jest wywoływana.
Jeśli dobrze to Rozumiem, różnica polega zasadniczo na tym, jak każda droga pobieraobjectref .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-23 11:06:00
Jedyny przypadek, który wiem, gdzie (2) może być lepszy, to użycie GWT, ponieważ zmniejsza footprint aplikacji(nie mój pomysł, ale zespół Google web toolkit tak mówi). Ale dla zwykĹ ' ej Javy pracujÄ ... cej w JVM (1) jest chyba zawsze lepiej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 07:54:29
Powiedziałbym, że 1 jest preferowane, chyba że
- zależy od implementacji opcjonalnego zachowania* w ArrayList, w takim przypadku jawne użycie ArrayList jest bardziej jasne
- będziesz używać ArrayList w wywołaniu metody, które wymaga ArrayList, ewentualnie dla opcjonalnego zachowania lub charakterystyki wydajności
Zgaduję, że w 99% przypadków możesz poradzić sobie z listą, która jest preferowana.
- na przykład
removeAll
, lubadd(null)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 07:47:58
List
interfejs posiada kilka różnych klas - ArrayList
i LinkedList
. LinkedList
służy do tworzenia indeksowanych kolekcji i ArrayList
- do tworzenia posortowanych list. Możesz więc użyć dowolnego z nich w swoich argumentach, ale możesz zezwolić innym programistom, którzy używają Twojego kodu, biblioteki itp. aby używać różnych typów List, nie tylko których używasz, więc w tej metodzie
ArrayList<Object> myMethod (ArrayList<Object> input) {
// body
}
Możesz używać go tylko z ArrayList
, a nie LinkedList
, ale możesz zezwolić na użycie dowolnej z List
klas w innych miejscach, w których używa się metody it, to tylko twój wybór, więc korzystanie z interfejsu może na to pozwolić:
List<Object> myMethod (List<Object> input) {
// body
}
W argumentach tej metody możesz użyć dowolnej klasy List
, której chcesz użyć:
List<Object> list = new ArrayList<Object> ();
list.add ("string");
myMethod (list);
Wniosek:
Używaj interfejsów wszędzie tam, gdzie jest to możliwe, nie ograniczaj siebie ani innych do używania różnych metod, których chcą używać.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 21:43:29