Что такое ArrayList в Java и каковы его основные характеристики?

Ответ

ArrayList в Java — это реализация интерфейса List, основанная на динамически изменяемом массиве. Это один из самых часто используемых контейнеров, который автоматически увеличивает свою внутреннюю емкость при добавлении элементов.

Основные характеристики:

  • Динамический размер: В отличие от обычного массива (int[]), размер ArrayList не фиксирован и меняется по мере необходимости.
  • Быстрый доступ по индексу: Получение элемента по индексу (get(index)) выполняется за константное время O(1), так как основано на обращении к массиву.
  • Медленные вставка и удаление в середину: При добавлении (add(index, element)) или удалении (remove(index)) элемента не в конце списка происходит сдвиг всех последующих элементов, что имеет сложность O(n).
  • Позволяет хранить null и дубликаты.
  • Не является синхронизированным (потокобезопасным). Для многопоточного доступа нужно использовать Collections.synchronizedList() или CopyOnWriteArrayList.

Пример использования:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Создание ArrayList
        ArrayList<String> languages = new ArrayList<>();

        // Добавление элементов
        languages.add("Java");
        languages.add("Kotlin");
        languages.add("Python");

        // Доступ по индексу
        System.out.println(languages.get(0)); // Вывод: Java

        // Удаление элемента (сдвигает последующие)
        languages.remove(1); // Удаляет "Kotlin"

        // Итерация
        for (String lang : languages) {
            System.out.println(lang); // Java, Python
        }
    }
}

Когда использовать ArrayList:

  • Когда требуется частый доступ к элементам по индексу (чтение).
  • Когда операции добавления/удаления происходят преимущественно в конце списка.
  • Когда заранее неизвестно количество элементов.

Когда рассмотреть альтернативу (LinkedList):

  • При частых вставках/удалениях в начало или середину списка.

Ответ 18+ 🔞

Ну, слушай сюда, про этот ваш ArrayList. Представь себе, блядь, обычный массив, вот этот int[], который как бетонная стена — раз построил и нихуя не сдвинешь. А теперь возьми и нахуярь ему мозгов, чтобы он сам умел растягиваться, как жопа после новогодних праздников. Вот это и есть ArrayList, ёпта!

Основные фишки, которые надо в голове держать, а то потом будешь руками разводить:

  • Размер динамический. Ты туда пихаешь элементы, а он, сука, сам себе место под новые находит. Внутри у него обычный массив, но когда место кончается, он такой: "Опа, щас новенький, побольше, нахуярим!" и копирует всё в новый массив. Умный, блядь, как мартышка с гранатой.
  • Доступ по индексу — просто космос. Хочешь элемент под номером 42? list.get(42) и готово, за константное время, потому что внутри — массив, а к нему по индексу доступ — раз плюнуть. O(1), хуле.
  • А вот впихнуть или выковырять что-то из середины — это пиздец. Представь, у тебя очередь в столовой, и ты решил встать ровно посередке. Всех, кто сзади, надо на шаг отодвинуть, сука! Вот и тут так же: при вставке или удалении в середине (add(index, element) или remove(index)) весь хвост списка сдвигается. Сложность O(n), то есть чем список длиннее, тем дольше эта движуха. Если делать это часто — пиши пропало, тормоза будут жуткие.
  • null и дубликаты — пожалуйста. Можешь пихать туда null до одури, можешь десять раз добавить слово "хуй" — ему похуй.
  • С потоками не дружит. Если с разных концов его начать дёргать — получишь race condition и прочую весёлую хуйню. Для таких дел есть Collections.synchronizedList() или CopyOnWriteArrayList, но это уже другая история, блядь.

Вот смотри, как этим пользоваться, на живом примере:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Создаём наш динамический массив-список для языков
        ArrayList<String> languages = new ArrayList<>();

        // Напихиваем в него всего, что придумали
        languages.add("Java");
        languages.add("Kotlin");
        languages.add("Python");

        // Хвать первый элемент по индексу — раз и готово!
        System.out.println(languages.get(0)); // Напечатает "Java"

        // А теперь выкинем Kotlin из середины. И всё поехало...
        languages.remove(1); // До свидания, "Kotlin"

        // Пройдёмся по тому, что осталось
        for (String lang : languages) {
            System.out.println(lang); // Выведет Java, потом Python
        }
    }
}

Так когда же его, блядь, использовать?

  • Когда тебе часто нужно читать элементы по их порядковому номеру. Это его конёк, ебать его в сраку.
  • Когда ты добавляешь новые элементы в основном в конец списка. Тогда никаких сдвигов не будет, всё быстро.
  • Когда с самого начала нихуя не понятно, сколько всего элементов наберётся.

А когда посмотреть в сторону LinkedList?

  • Когда у тебя постоянная движуха — вставки и удаления прямо в середину или в начало списка. Там уже другая структура, на связном списке, и сдвигать ничего не надо. Но доступ по индексу у неё, блядь, медленный. Так что выбирай, что тебе важнее: быстро искать или быстро перестраивать.