Lista typów vs Typ ArrayList w Javie [duplikat]

to pytanie ma już odpowiedzi tutaj : Co to znaczy "programować do interfejsu"? (32 odpowiedzi) Zamknięte 10 miesięcy temu .
(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.)

Author: catch23, 2010-02-17

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.

 459
Author: kgiannakakis,
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

Zastanawiam się, czy ktoś używa (2)?

Tak. Ale rzadko z rozsądnego powodu (IMO).

A ludzie się palą, bo używali ArrayList kiedy powinni używać List:

  • Metody użytkowe, takie jak Collections.singletonList(...) lub Arrays.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.)


Nie możesz napisać efektywnego kodu, jeśli nie wiesz, czy dostęp losowy jest skuteczny, czy nie.

To słuszna Uwaga. Jednak Java zapewnia lepsze sposoby radzenia sobie z tym problemem, np.]}
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.

Można też testować dynamicznie ... za pomocą 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.)

 119
Author: Stephen C,
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:

Tutaj wpisz opis obrazka

(posted by Schematy kolekcji)

 43
Author: Maxim Shoustin,
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ć.

 25
Author: catch23,
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);
    }
}
 14
Author: mazatwork,
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.

 14
Author: Denis Kutlubaev,
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.

 9
Author: True Soft,
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<>();
 9
Author: raudi,
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".

 9
Author: Pavel Vyazankin,
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:

  1. zawsze powinniśmy programować do interfejsu. Więcej tutaj .
  2. prawie zawsze będziesz używać ArrayList przez LinkedList. 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.

 7
Author: akhil_mittal,
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 .

 4
Author: Xar-e-ahmer Khan,
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 .

 3
Author: toxicafunk,
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.

 2
Author: Hans Westerbeek,
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, lub add(null)
 1
Author: extraneon,
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ć.

 1
Author: Acuna,
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