← Назад к вопросам

Когда лучше использовать ArrayList?

1.3 Junior🔥 141 комментариев
#Коллекции

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Когда лучше использовать ArrayList?

ArrayList — одна из наиболее часто используемых коллекций в Java. Давайте разберёмся, когда и почему её использовать.

Краткий ответ

ArrayList лучше использовать когда:

  • Нужно часто читать элементы по индексу (O(1) операция)
  • Часто добавляешь элементы в конец списка
  • Размер коллекции часто меняется
  • Порядок элементов важен

Полное объяснение

1. ArrayList: характеристики производительности

ArrayList основан на динамическом массиве:

get(index)      O(1) ✅ Очень быстро
add(element)    O(1) amortized - обычно O(1), редко O(n)
add(index, e)   O(n) - медленно, нужно сдвигать элементы
remove(index)   O(n) - медленно, нужно сдвигать элементы
contains(o)     O(n) - нужен линейный поиск

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

Сценарий 1: Много операций чтения

// ✅ ИСПОЛЬЗОВАТЬ ArrayList
List<String> cities = new ArrayList<>();
cities.add("New York");
cities.add("London");
cities.add("Tokyo");
cities.add("Paris");

// Много операций чтения по индексу
for (int i = 0; i < cities.size(); i++) {
    String city = cities.get(i);  // O(1) — очень быстро!
    System.out.println(city);
}

Сценарий 2: Добавление в конец

// ✅ ИСПОЛЬЗОВАТЬ ArrayList
List<Integer> numbers = new ArrayList<>();

// Много добавлений в конец
for (int i = 0; i < 1000000; i++) {
    numbers.add(i);  // O(1) amortized
}

Сценарий 3: Часто нужен размер коллекции

// ✅ ИСПОЛЬЗОВАТЬ ArrayList
List<Product> products = new ArrayList<>();
products.add(new Product("Laptop"));
products.add(new Product("Mouse"));
products.add(new Product("Keyboard"));

// Много операций size()
int count = products.size();  // O(1)
if (products.size() > 0) {
    Product first = products.get(0);  // O(1)
}

3. Когда НЕ использовать ArrayList

Сценарий 1: Много удалений/вставок в начало или середину

// ❌ НЕ ИСПОЛЬЗОВАТЬ ArrayList
List<String> queue = new ArrayList<>();
queue.add("Task 1");
queue.add("Task 2");
queue.add("Task 3");

// Много операций удаления с начала (очень медленно!)
while (!queue.isEmpty()) {
    String task = queue.remove(0);  // O(n) — нужно сдвигать все элементы!
    process(task);
}

// ✅ ИСПОЛЬЗОВАТЬ LinkedList вместо этого
Queue<String> queue = new LinkedList<>();
queue.add("Task 1");
queue.add("Task 2");
queue.add("Task 3");

while (!queue.isEmpty()) {
    String task = queue.poll();  // O(1) — быстро!
    process(task);
}

Сценарий 2: Много вставок в середину

// ❌ НЕ ИСПОЛЬЗОВАТЬ ArrayList
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);

// Много вставок в середину (очень медленно!)
for (int i = 0; i < 10000; i++) {
    numbers.add(1, i);  // O(n) — нужно сдвигать элементы каждый раз
}

// ✅ ИСПОЛЬЗОВАТЬ LinkedList вместо этого
List<Integer> numbers = new LinkedList<>();
// ...
numbers.add(1, i);  // O(1) — быстро!

4. Сравнение ArrayList vs LinkedList

ОперацияArrayListLinkedList
get(i)O(1) ✅O(n)
add(e)O(1) amortized ✅O(1) ✅
add(i, e)O(n)O(n) но обычно быстрее
remove(i)O(n)O(n) но обычнооычнее на краях
remove(o)O(n)O(n)
contains(o)O(n)O(n)
ПамятьМинимумБольше (две ссылки на элемент)

5. Практические примеры использования ArrayList

Пример 1: Простой список пользователей

// ✅ ArrayList идеален здесь
class UserService {
    private List<User> users = new ArrayList<>();
    
    public void addUser(User user) {
        users.add(user);  // O(1)
    }
    
    public User getUserById(int index) {
        return users.get(index);  // O(1)
    }
    
    public void printAllUsers() {
        for (User user : users) {  // Итерация эффективна
            System.out.println(user);
        }
    }
    
    public int getTotalUsers() {
        return users.size();  // O(1)
    }
}

Пример 2: Обработка данных из БД

// ✅ ArrayList идеален для хранения результатов запроса
class UserRepository {
    public List<User> findAll() {
        List<User> result = new ArrayList<>();
        
        // Читаем из БД и добавляем в конец
        ResultSet rs = executeQuery("SELECT * FROM users");
        while (rs.next()) {
            result.add(new User(rs.getInt("id"), rs.getString("name")));
        }
        
        return result;
    }
}

// Использование
UserRepository repo = new UserRepository();
List<User> users = repo.findAll();

// Много операций чтения
for (User user : users) {
    if (user.isActive()) {
        System.out.println(user);
    }
}

Пример 3: Кэширование результатов

// ✅ ArrayList для кэша с частым доступом
class CacheService<T> {
    private List<CacheEntry<T>> cache = new ArrayList<>();
    
    public T get(int index) {
        return cache.get(index).getValue();  // O(1)
    }
    
    public void put(T value) {
        cache.add(new CacheEntry<>(value));  // O(1)
    }
    
    public void clearOld() {
        // Удаляем старые записи
        // Если часто удаляешь — подумай о LinkedList
        cache.removeIf(entry -> entry.isExpired());
    }
}

6. Инициализация ArrayList с начальным размером

// ✅ Если знаешь примерный размер — задай capacity
List<Integer> numbers = new ArrayList<>(10000);
for (int i = 0; i < 10000; i++) {
    numbers.add(i);  // Не будет resizing, это быстрее!
}

// ❌ Без предварительного размера — будет много расширений
List<Integer> slow = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    slow.add(i);  // ArrayList будет resize несколько раз
}

7. Неизменяемые ArrayList (Immutable)

// ✅ Если не нужна изменяемость — используй unmodifiable list
List<String> immutable = Collections.unmodifiableList(
    new ArrayList<>(Arrays.asList("a", "b", "c"))
);

// Или в Java 9+
List<String> immutable = List.of("a", "b", "c");

// Попытка изменить вызовет UnsupportedOperationException
// immutable.add("d");  // Ошибка!

8. ArrayList в многопоточной среде

// ❌ ArrayList НЕ потокобезопасен
List<String> notSafe = new ArrayList<>();
notSafe.add("item");  // Race condition в многопоточной среде!

// ✅ Если нужна потокобезопасность
List<String> safe = Collections.synchronizedList(new ArrayList<>());

// ИЛИ используй CopyOnWriteArrayList для частого чтения
List<String> readHeavy = new CopyOnWriteArrayList<>();
readHeavy.add("item");  // Потокобезопасно

// ИЛИ используй Stream API с параллелизмом
List<String> list = new ArrayList<>();
list.stream()
    .parallel()  // Параллельная обработка
    .filter(item -> item.length() > 5)
    .collect(Collectors.toList());

9. Реальный пример: фильтрация данных

// ✅ ArrayList отлично подходит для этого
class UserFilter {
    public List<User> filterActiveUsers(List<User> allUsers) {
        List<User> activeUsers = new ArrayList<>();
        
        for (User user : allUsers) {
            if (user.isActive()) {
                activeUsers.add(user);  // O(1)
            }
        }
        
        return activeUsers;
    }
    
    // Или с Stream API
    public List<User> filterActiveUsersStream(List<User> allUsers) {
        return allUsers.stream()
            .filter(User::isActive)
            .collect(Collectors.toList());  // Возвращает ArrayList
    }
}

10. Когда выбрать другую коллекцию

ArrayList        → Много чтений по индексу, добавления в конец
LinkedList       → Часто удаляешь/добавляешь в начало или середину
HashSet          → Нужна уникальность, быстрый поиск, порядок не важен
TreeSet          → Нужна сортировка, уникальность
HashMap          → Нужны key-value пары, быстрый поиск по ключу
TreeMap          → Нужна сортировка по ключам
ConcurrentMap    → Многопоточность без явной синхронизации
Queue            → FIFO структура (используй LinkedList)
Deque            → FIFO или LIFO (LinkedList или ArrayDeque)

Ключевые выводы

  1. ArrayList лучший выбор для:

    • Частого чтения по индексу (get)
    • Добавления в конец (add)
    • Списков данных с неизменяемым или редко меняющимся размером
  2. ArrayList плохой выбор для:

    • Частого удаления/вставки в начало или середину
    • Больших коллекций где каждая операция критична
  3. Оптимизация:

    • Указывай начальный размер: new ArrayList<>(expectedSize)
    • Используй enhanced for loop для итерации
    • Предпочитай add(E) вместо add(int, E)
  4. Альтернативы:

    • LinkedList для очередей и частых вставок
    • HashSet для уникальности
    • Stream API для функциональной обработки
  5. Потокобезопасность:

    • ArrayList не потокобезопасен по умолчанию
    • Используй Collections.synchronizedList() или CopyOnWriteArrayList

ArrayList — это рабочая лошадка Java коллекций. Используй её по умолчанию, пока не понадобится специализированная структура.