JavaRush /Java блог /Random UA /Клас ArrayList в Java

Клас ArrayList в Java

Стаття з групи Random UA
Вітання! У попередніх лекціях ми детально розібрали таку структуру даних як масив і розглянули найпоширеніші приклади роботи з ними. Але ця структура даних має низку недоліків. Відповіддю на них у Java стала поява ArrayList. Якщо говорити максимально просто, то список ArrayList в Java - це "прокачаний" масив з великою кількістю нових можливостей.Клас ArrayList - 1

Чим Java Arraylist відрізняється від звичайних масивів?

Взагалі, масиви - штука досить зручна і як ти вже помітив, з ними можна багато чого робити :) Тим не менш, масиви мають і ряд недоліків.
  • Обмежений розмір. Потрібно вже на етапі створення масиву знати, скільки осередків він має містити. Недооціниш потрібну кількість — місця не вистачить. Переоціниш - масив залишиться напівпорожнім, і це ще півбіди. Адже виходить, ти ще й виділиш під нього більший обсяг пам'яті, ніж потрібно.
  • У масиву немає методів додавання елементів. Завжди доводиться явно вказувати індекс комірки, куди додати елемент. Якщо ненароком вказати вже зайняту комірку з якимось потрібним значенням, воно перезапишеться.
  • Немає методів видалення елемента. Значення можна лише "обнулити".
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("Томас");
       cats[1] = new Cat("Бегемот");
       cats[2] = new Cat("Філіп Маркович");

       cats[1] = null;



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

   @Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Висновок:

[Cat{name='Томас'}, null, Cat{name='Фабопп Маркович'}]
Всі ці недоліки можна усунути, використовуючи ArrayList. Створюється він дуже просто:
ArrayList<Cat> cats = new ArrayList<Cat>();
Тепер ми створабо список для зберігання об'єктів Cat. Зверни увагу:ми не вказуємо розмір ArrayList'a, оскільки він автоматично розширюється. Як таке можливо? Легко. Ти здивуєшся, але в основі ArrayList'a лежить звичайнісінький масив :) Так, всередині у нього знаходиться масив, в якому зберігаються наші елементи. Але у ArrayList'a є спеціальний механізм роботи з ним:
  • Коли цей внутрішній масив заповнюється, ArrayList створює в собі новий масив. Його розмір = (Розмір старого масиву * 1,5) +1.
  • Всі дані копіюються зі старого масиву в новий
  • Старий масив видаляється збирачем сміття.
Завдяки цьому механізму ArrayList'e (на відміну від масиву) реалізований метод для додавання нового елемента. Це метод add().
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<Cat>();
   cats.add(new Cat("Бегемот"));
}
Новий елемент додається до кінця списку. Тепер ризику переповнення немає, тому такий механізм є повністю безпечним. До речі, ArrayList вміє не тільки шукати об'єкт за індексом, а й навпаки – може знайти індекс об'єкта в ArrayList'і за посиланням на об'єкт! Для цього в ньому реалізований метод indexOf(): Ми передаємо в нього посилання на потрібний об'єкт, і indexOf()повертає нам його індекс:
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

   int thomasIndex = cats.indexOf(thomas);
   System.out.println(thomasIndex);
}
Висновок:

0
Все вірно, об'єкт thomasдійсно зберігається в комірці 0. У масивів є не лише недоліки, а й безперечні переваги. Один із них — пошук елемента за індексом. Оскільки ми вказуємо на індекс, тобто на конкретну адресау пам'яті, такий пошук у масиві здійснюється дуже швидко. ArrayList у Java теж так уміє! Для цього в ньому реалізовано метод get():
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

   Cat secondCat = cats.get(1);

   System.out.println(secondCat);
}
Висновок:

Cat{name='Бегемот'}
Крім того, можна легко дізнатися, чи містить ArrayList якийсь конкретний об'єкт, чи ні. Це робиться за допомогою методу contains():
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

   cats.remove(pushok);
   System.out.println(cats.contains(pushok));
}
Метод перевіряє, чи міститься елемент у внутрішньому масиві ArrayList'a, і повертає результат у вигляді boolean- trueабо false. Висновок:

false
І ще важливе з приводу вставки. ArrayList дозволяє вставляти дані не тільки в кінець масиву, але і в будь-яку комірку за індексом. Для цього він має два методи:
  • add(int index, Cat element)
  • set(int index, Cat element)
В обидва ти передаєш індекс осередку, в який потрібно зробити вставку, та посилання на сам об'єкт. Різниця в тому, що вставка через set()затирає старе значення, що зберігається в комірці. А вставка через add()спочатку зрушує всі елементи починаючи з [index]до кінця масиву, а в порожню комірку, що утворилася, додає потрібний тобі об'єкт. Ось приклад:
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

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

   cats.set(0, philipp);//Зараз у нас список із 2 котів. Додаємо 3-го через set:

   System.out.println(cats.toString());
}
Висновок:

[[Cat{name='Томас'}, Cat{name='Бегемот'}]
[Cat{name='Фабопп Маркович'}, Cat{name='Бегемот'}]
У нас був список з 2 котів, ми вставабо ще одного через метод set()у комірку 0. У результаті старе значення, що зберігається в цьому осередку, замінабо на нове.
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

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

   cats.add(0, philipp);//Зараз у нас список із 2 котів. Додаємо 3-го через add

   System.out.println(cats.toString());
}
А от add()спрацював інакше. Він зрушив усі елементи вправо і після цього записав нове значення в комірку 0. Висновок:

[Cat{name='Томас'}, Cat{name='Бегемот'}]
[Cat{name='Фабопп Маркович'}, Cat{name='Томас'}, Cat{name='Бегемот'}]
Щоб повністю очистити список, використовується метод clear():
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

   cats.clear();

   System.out.println(cats.toString());
}
Висновок:

[]
Зі списку було видалено весь вміст. До речі, зверніть увагу: на відміну від масивів, ArrayList метод toString() перевизначений і відразу виводить список у форматі рядка. У випадку масивів нам для цього доводилося використовувати клас Arrays. І раз вже згадали про Arrays: Java можна легко “перемикатися” між масивом і ArrayList, тобто перетворювати одне до іншого. У класі Arrays є метод Arrays.asList(). З його допомогою ми отримуємо вміст масиву у вигляді списку та передаємо його в конструктор нашого ArrayList:
public static void main(String[] args) {

   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

   Cat[] catsArray = {thomas, behemoth, philipp, pushok};

   ArrayList<Cat> catsList = new ArrayList<>(Arrays.asList(catsArray));
   System.out.println(catsList);
}
Висновок:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фабопп Маркович'}, Cat{name='Пушок'}]
Можна зробити і навпаки – отримати масив із об'єкта ArrayList. Для цього використовується метод toArray():
public static void main(String[] args) {

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

   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

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

   System.out.println(Arrays.toString(catsArray));
}
Зверніть увагу: у метод toArray() ми передали порожній масив. Це не помилка. Всередині класу ArrayList цей метод реалізований таким чином, що передача порожнього масиву збільшує його продуктивність. Поки що просто запам'ятай це на майбутнє (але передати якийсь конкретний розмір теж можна, буде працювати). До речі про розмір. Поточний розмір списку можна дізнатися за допомогою методу size():
public static void main(String[] args) {

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


   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

   System.out.println(cats.size());
}
Тут важливо розуміти, що, на відміну від властивості lengthмасиву, метод ArrayList.size() повертає саме кількість елементів, а не початкову місткість, адже її ми при створенні ArrayList не вказуємо. До речі, вказати її взагалі можна. ArrayList має відповідний конструктор. Але його поведінка щодо додавання нових елементів від цього не зміниться:
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>(2);//Створюємо ArrayList з початковою місткістю 2


   Cat thomas = new Cat("Томас");
   Cat behemoth = new Cat("Бегемот");
   Cat philipp = new Cat("Філіп Маркович");
   Cat pushok = new Cat("Пушок");

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

   System.out.println(cats.size());
}
Виведення в консоль:

4
Ми створабо список на два елементи, але коли нам це знадобилося, він спокійно розширився. Інша річ, якщо ми створабо спочатку дуже маленький список, йому доведеться частіше проводити операцію розширення, а це витрачає певну кількість ресурсів. У цій лекції ми майже не торкнулися процесу видалення елементів з ArrayList. Звичайно, це не через забудькуватість. Ця тема була виділена в окрему лекцію, з якою ти зможеш ознайомитись далі :)
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ