Interfejs List
Z podstawowych lekcji kursu dowiedzieliśmy się o prostej strukturze danych - tablicach. Niestety narzucają one na nas dużo ograniczeń, z których najważniejszym jest niezmienny rozmiar. Można to ominąć tworząc nową tablicę o większym rozmiarze i przekopiować do niej wszystkie elementy. Oczywiście rozwiązanie takie jest strasznie nieefektywne i efektywność aplikacji je wykorzystujące stałoby na niskim poziomie. Tablic powinniśmy używać tylko w przypadku, gdy znamy dokładną ilość przechowywanych elementów oraz wiemy, że nie ulegnie ona zmianie.
Na szczęście do dyspozycji mamy interfejs List, dzięki któremu możemy utworzyć różne struktury zwane listami. Wśród nich najważniejsze dla nas:
- ArrayList- implementacja tablicowa
- LinkedList- implementacja wiązana
Obie mają swoje wady i zalety o których jednak mowa będzie w osobnej lekcji dotyczącej list i ich implementacji. Najprościej mówiąc klasę ArrayList wykorzystujemy w przypadku, gdy najważniejszy jest czas dostępu do danych, natomiast LinkedList, gdy wiemy, że często będą wykonywane operacje usuwania, dodawania itp elementów gdzieś w środku struktury, a sprawą podrzędną są operacje wyszukiwania i szybkości dostępu do elementów listy.
Listy deklarujemy tak jak inne obiekty, warto jednak deklarować je jako List, a przypisywać im obiekt konkretnej klasy, dzięki temu nasz kod będzie bardziej uniwersalny i w przyszłości możemy podmienić implementację bez zmiany reszty kodu.
List lista1 = new ArrayList();
List lista2 = new LinkedList();
//w przypadku listy tablicowej warto dodać domyślny początkowy rozmiar - initialCapacity w konstruktorze
List lista3 = new ArrayList(32);
Podstawowe metody niezależne od implementacji to:
- add(Object) - dodaje element do listy
- remove(Object) - usuwa pierwsze wystąpienie podanego obiektu z listy
- remove(int) - usuwa z listy element o wskazanym indeksie
- size() - odpowiednik własności length w przypadku tablic - zwraca rozmiar listy
- get(int) - pozwala odczytać element o wskazanym indeksie
Przetestujmy działanie powyższych metod na liście typu ArrayList (w przypadku LinkedList byłoby identycznie).
import java.util.ArrayList;
import java.util.List;
public class Test{
public static void main(String[] args){
//tworzymy tablicę
List<String> lista = new ArrayList<String>();
//dodajemy elementy typu Object - czyli dowolne, my dodamy ciągi znaków
lista.add("Asia");
lista.add("Basia");
lista.add("Krzysiek");
lista.add("Wojtek");
//sprawdzamy rozmiar listy
System.out.println("Rozmiar listy to: "+lista.size());
//usuwamy obiekt "Asia" i sprawdzamy rozmiar
lista.remove("Asia");
System.out.println("Rozmiar listy to: "+lista.size());
//usuwamy obiekt zajmujący 2 miejsce, czyli o indeksie 1 - u nas "Krzysiek"
lista.remove(1);
System.out.println("Rozmiar listy to: "+lista.size());
//na koniec przypiszmy pierwszy element listy zmiennej imie i wyświetlmy go
//musimy użyć rzutowania, ponieważ inaczej próbowalibyśmy przypisać Object do String
String imie = (String)lista.get(0);
System.out.println("Pierwszy element listy to: "+imie);
}
}
Oczywiście jest też kilka innych przydatnych metod dotyczących na przykład pierwszych i ostatnich elementów w liście - bo do nich najczęściej są odwołania.
Ponieważ Listy są klasami generycznymi to powinniśmy im nadać konkretny typ (w komentarzu ktoś ukazał, że standardowy kompilator wręcz tego wymaga w obecnej wersji). W naszym powyższym przykładzie mogliśmy utworzyć taką listę tablicową:
List<String> lista = new ArrayList<String>();
a od Javy 7 możemy to nawet skrócić do:
List<String> lista = new ArrayList<>();
Dzięki takiemu zabiegowi pozbylibyśmy się konieczności rzutowania obiektów z typu Object na niższe typy, a co za tym idzie uprościliśmy kod. W przypadku, gdy chcielibyśmy zrobić następujące przypisanie:
String zmienna = lista.get(1);
wcześniej otrzymalibyśmy błąd, ponieważ próbowalibyśmy przypisać typ Object do typu String, dzięki określeniu typu generycznego wskazaliśmy, że w liście przechowujemy Stringi i problem ten znika.
Dyskusja i komentarze
Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.
Poniżej znajdziesz archiwalne wpisy z czasów, gdy strona była jeszcze hobbystycznym blogiem.
YeeeZooo
Kod się nie kompiluje, trzeba jednak dodać to co piszesz o klasie generycznej i wtedy działa, chyba że coś robię źle.
Slawek
Kod powinien kompilować i uruchamiać się bez problemu, dodanie typu generycznego służy tylko konkretyzacji typu listy. Coś musiałeś chyba źle zrobić, bo przed chwilą to uruchomiłem :) Lekcja o interfejsach jest częściowo ukończona, mam nadzieję w weekend uda mi się ją dokończyć, podobnie lekcja o wyjątkach.
YeeeZooo
kopiuję cały kod ze strony jedynie nazwę klasy zmieniam na Interfejs i wyrzuca taki błąd: Note: Interfejs.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
Slawek
Faktycznie zobaczyłem teraz, że od Javy 5 standardowy kompilator Javy rzuca ostrzeżenia przy próbie działania na listach, bez specyfikacji typu. Java jest językiem bezpiecznym i statycznie typizowanym, a w takim wypadku nie może sprawdzić typu. Co ciekawe w eclipse program działa ok. Więc i ja się czegoś nauczyłem :) Artykuł do lekkiej poprawki.
YeeeZooo
Można to dać na główną stronę :) http://www.joemonster.org/filmy/26280/Java_4_ever Swoją drogą brak mechanizmu powiadomień np: odpowiedzi na posta, za każdym razem muszę przeszukiwać wszystko czy ktoś gdzieś mi nie odpowiedział.
Jacek
Sławku jak do tej pory genialny kurs. Przerabiam skrupulatnie, jednak w ostatnich lekcjach zaczęło brakować mi zadań do samodzielnego wykonania : ) Korzystając z okazji chciałbym zadać pytanie. Jako, że w przyszłości planuje raczej korzystać z NB niz Eclipse, może mógłbym prosić o polecenie materiałów szkoleniowych mogących stanowić rozszerzenie tego kursu Pozdrawiam i z góry dziękuję
Marek
Witam wszystkich. Irytacja już mi minęła, więc piszę; mianowicie znalazłem błąd w tej lekcji. Tzn. nie tyle błąd ile niedopatrzenie, które może (tak jak w moim przypadku) prowadzić do niepotrzebnych frustracji. Dzięki konstrukcji 'List lista1 = new ArrayList(); List lista2 = new LinkedList();' Kod może i jest bardziej uniwersalny, ale nigdzie nie zostało wspomniane, że deklarując obiekt jako 'List', nawet gdy użyjemy konstruktora klasy potomnej, mamy dostęp jedynie do metod klasy 'List'. Sprawiło mi to sporo kłopotów, ponieważ przy stosowaniu metod klas potomnych (np. removeLast()) kompilator rzucał błędem. Prosiłbym o wyjaśnienie tej kwestii w kursie
LOCODE
dzięki! tablica działa! ale jak zrobić drugi wymiar ?
andrew
Witam. Jak utworzyć kilka list, ale ilość będzie pobierana z klawiatury? Próbuje coś takiego: <code> int i=in.nextInt(System.in) List lista[i] = new ArrayList(); </code> wiem, że nie działa, ale obrazu to co chce zrobić. Może ktoś pomoże? Pozdro
Slawek
Generalnie to <pre class="java"> List[] listy = new List[ilość]; listy[0] = new LinkedList(); ... </pre> Ale nie polecam czegoś takiego. Zdecydowanie lepiej będzie używać po prostu listy list, korzystając z typu generycznego.
Piszczu
"Najprościej mówiąc klasę ArrayList wykorzystujemy w przypadku, gdy najważniejszy jest czas dostępu do danych, natomiast LinkedList, gdy wiemy, że często będą wykonywane operacje usuwania, dodawania itp elementów gdzieś w środku struktury" - jeżeli ArrayList to odpowiednik Vector z C++ a LinkedList to List z C++ (a to wnioskuje po artykule), to to zdanie jest całkowitą głupotą. LinkedList stosujemy tylko wtedy kiedy ważna jest kolejność elementów przy dodawaniu i usuwaniu z listy. Robimy to z bardzo prostego powodu, jeżeli chcesz usunąć element ze środka ArrayList i nie zależy ci na kolejności, wtedy zamieniasz go wartościami z ostatnim elementem i kasujesz ten który będzie na końcu, co jest rozwiązaniem dużo szybszym niż dostęp do kasowanego elementu w LinkedList.
Sławek Ludwiczak
W Javie jest Vector i ArrayList jako oddzielne struktury - różnią się przede wszystkim tym, że Vector nie jest strukturą synchronizowaną. Przy usuwaniu elementów z środka ArrayListy w Javie wszystkie elementy leżące dalej są kopiowane na miejsce poprzedzające - co jak można się domyślić nie jest zbyt wydajne. W LinkedList zmieniana jest jedna referencja. Gdyby było tak, że w wolne miejsce zostawałaby wpisywana wartość z ostatniego elementu listy to tracilibyśmy jej podstawową funkcjonalność (po tablicach i listach zazwyczaj iterujemy - więc kolejność w większości przypadków jak najbardziej ma znaczenie). Jeśli komuś jednak bardzo zależy na rozwiązaniu takim jak sugerujesz, to nic nie stoi na przeszkodzie, żeby zaimplementować własną listę.
Doc
Witam serdecznie! Jestem początkujący i uczę się Javy od kilku tygodni, również z tej świetnej strony - Sławku, gratulacje i dzięki! Ośmielę się jednak nie zgodzić z Twoją wypowiedzią w poprzednim poście: "W Javie jest Vector i ArrayList jako oddzielne struktury – różnią się przede wszystkim tym, że Vector nie jest strukturą synchronizowaną." Jest dokładnie na odwrót - Vector jest synchronizowany, a ArrayList nie jest. Pozdrowienia!:)
Sławek Ludwiczak
Tfu, oczywiście dzięki za sprostowanie :) Jeszcze uzupełniając jaką to powoduje różnicę - przede wszystkim widoczne będzie to w wydajności (Vector będzie kilka, lub nawet kilkanaście razy wolniejszy od ArrayListy z Collections).
Dawid
<blockquote> <a href="#comment-561" rel="nofollow"> <strong><em>Marek:</em></strong> </a> Witam wszystkich. Irytacja już mi minęła, więc piszę; mianowicie znalazłem błąd w tej lekcji. Tzn. nie tyle błąd ile niedopatrzenie, które może (tak jak w moim przypadku) prowadzić do niepotrzebnych frustracji. Dzięki konstrukcji ‚List lista1 = new ArrayList(); List lista2 = new LinkedList();’ Kod może i jest bardziej uniwersalny, ale nigdzie nie zostało wspomniane, że deklarując obiekt jako ‚List’, nawet gdy użyjemy konstruktora klasy potomnej, mamy dostęp jedynie do metod klasy ‚List’. Sprawiło mi to sporo kłopotów, ponieważ przy stosowaniu metod klas potomnych (np. removeLast()) kompilator rzucał błędem. Prosiłbym o wyjaśnienie tej kwestii w kursie </blockquote> refresh
Wiggy
ArrayList lista1 = new ArrayList(); i już masz dostęp do metod klasy "nazwa_klasy"