CodeGym /Blog Java /Java Developer /Klasa ArrayList
Autor
Volodymyr Portianko
Java Engineer at Playtika

Klasa ArrayList

Opublikowano w grupie Java Developer
Cześć! W poprzednich lekcjach zagłębiliśmy się w tablice i zapoznaliśmy się z typowymi przykładami pracy z nimi. Ogólnie rzecz biorąc, tablice są bardzo przydatne. Jak już zauważyliście, można z nimi wiele zrobić :) Mają one jednak pewne wady.
  • Ograniczony rozmiar. W momencie tworzenia tablicy trzeba wiedzieć, ile elementów ma ona zawierać. Jeśli nie doszacujesz, nie wystarczy ci miejsca. Zawyżanie wartości spowoduje, że tablica pozostanie w połowie pusta, co również jest niekorzystne. Koniec końców i tak przydziela się więcej pamięci, niż jest to konieczne.

  • Tablica nie ma metod umożliwiających dodawanie elementów. Należy zawsze jawnie podać indeks pozycji, do której ma zostać dodany element. Jeśli przez pomyłkę podasz indeks dla pozycji zajmowanej przez jakąś potrzebną wartość, zostanie ona nadpisana.

  • Nie ma metod umożliwiających usunięcie elementu. Wartość można jedynie „wyzerować”.


public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat[] cats = new Cat[3];
       cats[0] = new Cat("Thomas");
       cats[1] = new Cat("Behemoth");
       cats[2] = new Cat("Lionel Messi");

       cats[1] = null;

      
      
       System.out.println(Arrays.toString(cats));
   }

   @Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Wydruk:
[Cat{imię='Tomasz'}, null, Cat{imię='Lionel Messi'}]
Na szczęście twórcy Javy doskonale zdają sobie sprawę z zalet i wad tablic, dlatego stworzyli bardzo interesującą strukturę danych o nazwie ArrayList. Mówiąc najprościej, jak to tylko możliwe, ArrayList to „podrasowana” tablica z wieloma nowymi funkcjami. Tworzenie jej jest bardzo proste:

ArrayList<Cat> cats = new ArrayList<Cat>();
Teraz utworzyliśmy listę do przechowywania obiektów Cat. Zwróć uwagę, że nie określamy rozmiaru listy ArrayList, ponieważ może się ona rozszerzać automatycznie. Jak to jest możliwe? W rzeczywistości jest to dość proste. Może cię to zaskoczy, ale ArrayList jest zbudowana na fundamentach zwyczajnej tablicy :) Tak, klasa ta zawiera tablicę, w której przechowywane są nasze elementy. Dodatkowo ArrayList ma specjalny sposób pracy z taką tablicą:
  • Gdy wewnętrzna tablica zostanie zapełniona, ArrayList tworzy nową tablicę wew nątrz tablicy już istniejącej. Rozmiar nowej tablicy jest równy rozmiarowi starej pomnożonej przez 1,5 plus 1.

  • Wszystkie dane są kopiowane ze starej tablicy do nowej

  • Stara tablica jest czyszczona przez śmieciarkę (ang. garbage collector).
Klasa ArrayList - 1Ten mechanizm pozwala ArrayList (w przeciwieństwie do zwykłej tablicy) na zaimplementowanie metody dodawania nowych elementów. Jest to metoda add():
add()
Nowe pozycje są dodawane na końcu listy. Teraz ryzyko przepełnienia tablicy zniknęło, więc metoda ta jest całkowicie bezpieczna. Przy okazji, ArrayList może nie tylko znaleźć obiekt po jego indeksie, ale także odwrotnie: może użyć referencji, aby znaleźć indeks obiektu w ArrayList! Do tego właśnie służy metoda indexOf(): Przekazujemy referencję do poszukiwanego obiektu, a indexOf() zwraca jego indeks:

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<Cat>();
   cats.add(new Cat("Behemoth"));
}
Wydruk:
0
Zgadza się. Nasz obiekt span class="code">tomasz jest rzeczywiście przechowywany w elemencie 0. Tablice mają nie tylko wady. Posiadają również niewątpliwe zalety. Jedną z nich jest możliwość wyszukiwania elementów według indeksu. Ponieważ wskazujemy na indeks, czyli na konkretny adres pamięci, przeszukiwanie tablicy w ten sposób jest bardzo szybkie. ArrayList też to potrafi! Implementuje to metoda get():

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   int thomasIndex = cats.indexOf(thomas);
   System.out.println(thomasIndex);
}
Wydruk:
Cat{imię='Behemoth'}
Ponadto można łatwo sprawdzić, czy ArrayList zawiera konkretny obiekt. Służy do tego metoda public static void main(String[] args) { ArrayList<Cat> cats = new ArrayList<>(); Cat thomas = new Cat("Thomas"); Cat behemoth = new Cat("Behemoth"); Cat lionel = new Cat("Lionel Messi"); Cat fluffy = new Cat ("Fluffy"); cats.add(thomas); cats.add(behemoth); cats.add(lionel); cats.add(fluffy); Cat secondCat = cats.get(1); System.out.println(secondCat); } :

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   cats.remove(fluffy);
   System.out.println(cats.contains(fluffy));
}
Metoda sprawdza, czy wewnętrzna tablica ArrayList zawiera dany element i zwraca typ boolean (true lub false). Wydruk:
false
I jeszcze jedna ważna rzecz dotycząca wstawiania elementów. ArrayList pozwala używać indeksu do wstawiania elementów nie tylko na końcu tablicy, ale także w dowolnym jej miejscu. Służą do tego dwie metody:
  • add(int index, Cat element)
  • set(int index, Cat element)
Obie te metody przyjmują jako argumenty indeks pozycji, do której chcemy wstawić obiekt oraz referencję do samego obiektu. Różnica polega na tym, że wstawianie za pomocą set() nadpisuje starą wartość. Wstawianie za pomocą add() najpierw przesuwa o jeden wszystkie elementy zaczynając od [indeks] do końca tablicy, a następnie dodaje podany obiekt na powstałej w ten sposób pustej pozycji. Oto przykład:

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);

   System.out.println(cats.toString());

   cats.set(0, lionel);// Now we have a list of 2 cats. Adding a 3rd using set

   System.out.println(cats.toString());
}
Wydruk:
[[Cat{imię='Tomasz'}, Cat{imię='Behemoth'}] [Cat{imię='Lionel Messi'}, Cat{imię=''Behemoth''}]
Mieliśmy listę 2 kotów. Następnie wstawiliśmy kolejnego jako element 0 za pomocą metody set(). W efekcie stary element został zastąpiony nowym.

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);

   System.out.println(cats.toString());

   cats.add(0, lionel);// Now we have a list of 2 cats. Adding a 3rd using add

   System.out.println(cats.toString());
}
Tu widzimy, że add() działa inaczej. Przesuwa wszystkie elementy w prawo, a następnie zapisuje nową wartość jako element o indeksie 0. Wydruk:
[Cat{imię='Tomasz'}, Cat{imię='Behemoth'}] [Cat{imię='Lionel Messi'}, Cat{imię='Tomasz'}, Cat{imię='Behemoth'}]
Aby całkowicie wyczyścić listę, używamy metody clear():

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   cats.clear();

   System.out.println(cats.toString());
}
Wydruk:
[]
Wszystko zostało usunięte z listy. Przy okazji zauważ że w przeciwieństwie do tablic, ArrayList nadpisuje metodę toString() i wyświetla listę w odpowiedniej formie, tj. jako ciągi. W przypadku zwykłych tablic trzeba było do tego celu użyć klasy Arrays. Więc skoro już wspomniano o Arrays: Java pozwala łatwo „przełączać” pomiędzy tablicą a ArrayList, tj. konwertować jedną na drugą. Klasa Arrays ma do tego metodę Arrays.asList(). Używamy jej, aby pobrać zawartość jako tablicę i przekazać ją do naszego konstruktora ArrayList:

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();


   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   Cat[] catsArray = {thomas, behemoth, lionel, fluffy};

   ArrayList<Cat> catsList = new ArrayList<>(Arrays.asList(catsArray));
   System.out.println(catsList);
}
Wydruk:
[Cat{imię='Thomas'}, Cat{imię='Behemoth'}, Cat{imię='Lionel Messi'}, Cat{imię='Puszek'}]
Można też pójść w przeciwnym kierunku: uzyskać tablicę z obiektu ArrayList. Robimy to za pomocą metody toArray():

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();

   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   Cat[] catsArray = cats.toArray(new Cat[0]);

   System.out.println(Arrays.toString(catsArray));
}
Uwaga: przekazaliśmy pustą tablicę do metody toArray(). To nie jest błąd. Wewnątrz klasy ArrayList metoda ta jest zaimplementowana w taki sposób, że przekazanie pustej tablicy zwiększa jej wydajność. Pamiętaj o tym na przyszłość (oczywiście możesz przekazać tablicę o określonym rozmiarze; to również zadziała). A propos wymiarów. Aktualną wielkość listy można sprawdzić za pomocą metody size():

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();


   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   System.out.println(cats.size());
}
Ważne jest, aby zrozumieć, że w przeciwieństwie do właściwości length tablicy, metoda ArrayList.size() zwraca rzeczywistą liczbę elementów, a nie pierwotną pojemność. W końcu nie określiliśmy rozmiaru podczas tworzenia ArrayList. Można go jednak określić — ArrayList ma odpowiedni konstruktor. Jednak nie zmienia to jej zachowania przy dodawaniu nowych elementów:

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>(2);// create an ArrayList with an initial capacity of 2


   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   System.out.println(cats.size());
}
Wydruk konsoli:
4
Utworzyliśmy listę składającą się z 2 elementów, która w razie potrzeby spokojnie może zostać powiększona. Inną kwestią jest to, że jeśli początkowo utworzymy bardzo małą listę, będzie ona musiała się rozszerzać częściej, co zużyje pewne zasoby. W tej lekcji prawie nie poruszyliśmy tematu usuwania elementów z ArrayList Oczywiście nie dlatego, że wypadło nam to z głowy. Ten temat umieściliśmy w osobnej lekcji, którą poznasz później :)
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION