Różnica pomiędzy List<> i ArrayList<>

0

Co zyskujemy pisząc

List<?> myList = new ArrayList<?>();

zamiast

ArrayList<?> myList = new ArrayList<?>();
2

W pierwszej opcji nie ograniczasz się do jednej implementacji.

Zauważ że List to jest interfejs. Pisząc z lewej strony List możesz potem do tej zmiennej podstawić inna implementacje List, a nie tylko ArrayList.

2
Alexandr.r napisał(a):

Co zyskujemy pisząc

List<?> myList = new ArrayList<?>();

zamiast

ArrayList<?> myList = new ArrayList<?>();

W takiej formie właściwie nie ma różnicy. Większość pisze to co jest krótsze.

Natomiast podczas deklarowania parametrów, lepiej zadeklarować przyjmowanie List<> zamiast ArrayList<>, by móc przekazać polimorficznie dowolną implementację List.

0
Riddle napisał(a):

W takiej formie właściwie nie ma różnicy. Większość pisze to co jest krótsze.

Ogólnie zgoda, natomiast jest jeden mały fakt: zapis ogólny oznacza, że nie można wykorzystać metod właściwych dla ArrayList.

Inna sprawa, że pisanie tego, co jest krótsze nie zawsze jest dobre. Czasami lepiej napisać ArrayList<?> myList = new ArrayList<>(capacity) by dać światu znać, że kod został napisany z myślą o ArrayList.

0
wartek01 napisał(a):
Riddle napisał(a):

W takiej formie właściwie nie ma różnicy. Większość pisze to co jest krótsze.

Ogólnie zgoda, natomiast jest jeden mały fakt: zapis ogólny oznacza, że nie można wykorzystać metod właściwych dla ArrayList.

Jeśli chcemy wykorzystać metod właściwych dla ArrayList, to całe to pytanie nie ma sensu.

0

Let me Chat GPT that for you: screenshot-20230320062852.png

6

@0xmarcin: Nie lubię tych odpowiedzi z ChatGPT, bo one się dzielą na dwie kategorię.

Zadajesz pytanie, i bot odpowiada:

Skąd pytający ma wtedy wiedzieć czy chat odpowiedział coś poprawnie czy jakiś bezsens?

0

W programowaniu warto określać to co myślisz najlepiej jak się da. Jeśli w danym miejscu nie dbasz o to jakiej implementacji listy używasz to użyj generycznego typu. W praktycznie każdym wypadku tak jest, bo wybór implementancji jest zazwyczaj podyktowany charakterystyką wydajnościową i jej użycie jest takie samo niezależnie od implementacji pod spodem

0
Alexandr.r napisał(a):

Co zyskujemy pisząc

List<?> myList = new ArrayList<?>();

zamiast

ArrayList<?> myList = new ArrayList<?>();

Tak na prawdę powinieneś zrobić krok wstecz i zapoznać się z polimorfizmem, bo na tym polega cała sztuczka. Bez tego ani rusz.

0
Alexandr.r napisał(a):

Co zyskujemy

Dodatkowy punkt na rozmowie kwalifikacyjnej.

0

No właśnie chodziło mi o zysk z tego tytułu - pisząc List<?> list = new ArrayList<>() zyskujesz gwarancję, że nikt nie będzie korzystał z metod właściwych dla ArrayList. Może to mieć znaczenie przy np. refaktorze.

1
wartek01 napisał(a):

No właśnie chodziło mi o zysk z tego tytułu - pisząc List<?> list = new ArrayList<>() zyskujesz gwarancję, że nikt nie będzie korzystał z metod właściwych dla ArrayList. Może to mieć znaczenie przy np. refaktorze.

Weeeź, daj spokój, to jest jakiś frazes.

Nie jest żadna "gwarancja", jak ktoś będzie chciał użyć metod z ArrayList to po prostu zmieni zadeklarowany typ i tyle. Poza tym, nawet jeśli - to po co utrudniać programistom pracę. Jeśli ktoś chce użyć metod z ArrayList to niech sobie użyje, jezu. Po to są.

Jak już piałem, przyjęcie w argumencie List<> nawet ma sens, ale w deklaracji raczej mały. Od javy 11tki i tak ludzie piszą var, a on implicitly wsadzi ArrayList. Jak mówiłem - większość pisze List bo jest krótsze i nikt się nad tym nie zastanawia dwóch sekund.

0
Alexandr.r napisał(a):

Co zyskujemy pisząc

List<?> myList = new ArrayList<?>();

zamiast

ArrayList<?> myList = new ArrayList<?>();

List to interface, który określa co robi implementujący go obiekt. ArrayList to jedna wielu możliwych implementacji tego interface'u, która jest dość szybka w dodawaniu elementów do końca, ale bardzo słaba w zmianach wewnątrz. Jeżeli w którymś momencie stwierdzisz, że większość operacji to dodawanie elementów do początku i usuwanie ich z końca, to możesz sobie bezkarnie podmienić ArrayList na LinkedList. Całość będzie robić dalej to samo, ale w niektórych przypadkach szybciej.
Dodatkowo, jeżeli będziesz np. potrzebował umieścić tę listę na zewnętrznym serwerze, to możesz sobie napisać własną implementację, która zamiast trzymać dane w pamięci, umieści i pobierze je z np. Redisa.

0
piotrpo napisał(a):
Alexandr.r napisał(a):

Co zyskujemy pisząc

List<?> myList = new ArrayList<?>();

zamiast

ArrayList<?> myList = new ArrayList<?>();

List to interface, który określa co robi implementujący go obiekt. ArrayList to jedna wielu możliwych implementacji tego interface'u, która jest dość szybka w dodawaniu elementów do końca, ale bardzo słaba w zmianach wewnątrz. Jeżeli w którymś momencie stwierdzisz, że większość operacji to dodawanie elementów do początku i usuwanie ich z końca, to możesz sobie bezkarnie podmienić ArrayList na LinkedList. Całość będzie robić dalej to samo, ale w niektórych przypadkach szybciej.
Dodatkowo, jeżeli będziesz np. potrzebował umieścić tę listę na zewnętrznym serwerze, to możesz sobie napisać własną implementację, która zamiast trzymać dane w pamięci, umieści i pobierze je z np. Redisa.

Nie ma znaczenia czy List to interfejs, klasa abstrakcyjna czy klasa bazowa w tym wypadku.

Użycie List ma tak na prawdę tylko dwie sensowne konsekwencje:

  • Dependency Inversion, jeśli użyjemy List w bibliotece w .jar, to nie musimy go recompilować jeśli chcemy zmienić wystawioną implementację
  • Open/Close, jeśli nasza funkcja przyjmuje parametr List, to możemy ją wywołać z dwoma parametrami na raz w jednym programie (np x(new ArrayList()) oraz x(new LinkedList()))

Poza tymi dwoma rzeczami, nie ma znaczenia której użyjemy. Dostajesz tutaj odpowiedzi z List możesz bezkarnie zmieniać implementacje, ale tak samo możesz z ArrayList - zmiana ArrayList na LinkedList dla ogarniętej osoby to jest max. 5 sekund.

0

@Riddle: No trochę jednak ma znaczenie. Abstrakcje na zewnątrz powinny być jak najmniej szczegółowe, żeby nie ograniczać niepotrzebnie możliwości manipulowania implementacją. Ale faktycznie w przypadku tego konkretnego tematu jest to raczej pusta dyskusja.

0
piotrpo napisał(a):

@Riddle: No trochę jednak ma znaczenie. Abstrakcje na zewnątrz powinny być jak najmniej szczegółowe, żeby nie ograniczać niepotrzebnie możliwości manipulowania implementacją. Ale faktycznie w przypadku tego konkretnego tematu jest to raczej pusta dyskusja.

Nie ma. Przypisuje się zbyt duże znaczenie temu czy zadeklarujesz zmienną jako List czy ArrayList. Intencja za nimi jest słuszna -> jeśli chcemy iść w DI lub O/C, ale sama deklaracja nie ma znaczenia.

Jeśli wszystkie argumenty tutaj wziąć na serio (czyli te wasze "gwarancje", intencje, zmiany implementacji) to powinniśmy deklarować wszystkie te zmienne jako Collection<> albo Enumerable<> - jeśli słuchać wymienionych rad tutaj, deklaracja jako Collection<> dałaby jeszcze większą wolność w zmianie implementacji - czemu nikt tego nie robi? No głównie dlatego że List<> szybciej się pisze, i to jest główny powód czemu jest stosowane.

Jeśli typ interfejsu byłby dłuższy niż imeplementacja (np ListInterface i = new List<>();) to gwarantuje Ci że 99% programistów i tak pisałoby List l = new List();.

0
Riddle napisał(a):

Weeeź, daj spokój, to jest jakiś frazes.

Żaden frazes, a częsta praktyka.
Masz rację w przypadku szczególnym, tj. takim, że w przypadku list to nie ma żadnego znaczenia. Wynika to z prostego faktu - tych metod się dzisiaj prawie nie używa. Ba, dzisiaj bardzo rzadko używa się dostępu przez indeks, jedynie z add() i stream() czy forEach().
Natomiast w przypadku ogólnym, takie ukrywanie konkretnej implementacji może mieć znaczenie - i jeśli ktoś próbuje to zmienić to jest to niezły sygnał ostrzegawczy przy code review.

jeśli słuchać wymienionych rad tutaj, deklaracja jako Collection<> dałaby jeszcze większą wolność w zmianie implementacji - czemu nikt tego nie robi? No głównie dlatego że List<> szybciej się pisze, i to jest główny powód czemu jest stosowane.

No ogólnie gdyby ludzie tak pisali, to łamaliby zasadę Liskowej - i pewnie część z nich poniosłaby konsekwencje.
Taki prosty test:

Collection<Integer> c = getImplementation();
c.add(1);
c.add(1);

assertEquals(2, c.size());

wywali się w zależności od tego, co zwróci getImplementation(). Gdy zamienisz Collection na List to jeśli nie użyjesz jakiejś egzotycznej (tj. popapranej) implementacji to ci zadziała.

1 użytkowników online, w tym zalogowanych: 0, gości: 1