← Назад к вопросам
Где хранятся ссылки в ArrayList?
1.3 Junior🔥 201 комментариев
#JVM и управление памятью#Коллекции
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Где хранятся ссылки в ArrayList?
Краткий ответ
Ссылки в ArrayList хранятся в обычном массиве Java (Object[]). Внутренняя реализация ArrayList — это обёртка над примитивным массивом с логикой динамического увеличения размера.
Внутренняя структура ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E> {
// Основной массив для хранения элементов
transient Object[] elementData; // Сюда хранятся ссылки
// Текущее количество элементов
private int size;
// Начальная ёмкость по умолчанию
private static final int DEFAULT_CAPACITY = 10;
}
Как это работает на уровне памяти
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
list.add("Java");
// Внутренний массив выглядит так:
// elementData = ["Hello", "World", "Java", null, null, null, ...]
// ^ ^ ^ ^
// ref ref ref пусто
Детальный взгляд на память
В Heap памяти:
// Объект ArrayList занимает место в Heap:
ArrayList<String> list = new ArrayList<>();
// ↓ Объект ArrayList в памяти
// ┌─────────────────┐
// │ elementData[] │ ──→ ссылка на массив Object[]
// │ size = 0 │
// │ capacity = 10 │
// └─────────────────┘
//
// Array в памяти:
// ┌────────────────────────────────────────┐
// │ null │ null │ null │ ... │ null │ │ Размер = 10
// └────────────────────────────────────────┘
list.add("Hello");
// ┌────────────────────────────────────────┐
// │ "Hello" │ null │ null │ ... │ null │ │
// └────────────────────────────────────────┘
// ↑ ссылка на строку в памяти
Как растёт ArrayList
public void ensureCapacity(int minCapacity) {
// Когда нужно больше места
if (minCapacity > elementData.length) {
// Увеличиваем ёмкость на 50%
int newCapacity = elementData.length + (elementData.length >> 1);
Object[] newElementData = new Object[newCapacity];
// Копируем все ссылки в новый массив
System.arraycopy(elementData, 0, newElementData, 0, size);
// Заменяем старый массив новым
elementData = newElementData;
}
}
Пример роста ArrayList
List<Integer> list = new ArrayList<>(); // Capacity = 10
for (int i = 0; i < 15; i++) {
list.add(i);
}
// Процесс добавления:
// После add(0-9): capacity = 10, size = 10
// При add(10): capacity = 15, size = 11 (10 + 10/2 = 15)
// После add(11-14): capacity = 15, size = 15
// При add(15): capacity = 22, size = 16 (15 + 15/2 = 22)
Внутренний массив доступен через Reflection
import java.lang.reflect.Field;
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
try {
// Получаем доступ к приватному массиву
Field field = ArrayList.class.getDeclaredField("elementData");
field.setAccessible(true);
Object[] elementData = (Object[]) field.get(list);
System.out.println("Array size: " + elementData.length);
System.out.println("Element 0: " + elementData[0]); // "Hello"
System.out.println("Element 1: " + elementData[1]); // "World"
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
Где находится сам массив Object[]?
// Массив Object[] хранится:
// 1. Сама ссылка на массив (в объекте ArrayList) → в HEAP
// 2. Сам массив Object[] → в HEAP
// 3. Объекты, на которые указывают ссылки → в HEAP
String s1 = "Hello";
String s2 = "World";
List<String> list = new ArrayList<>();
list.add(s1);
list.add(s2);
// Визуально в памяти:
// STACK HEAP
// ┌─────────────┐ ┌──────────────────────┐
// │ s1 ref ────┼───→│ "Hello" String obj │
// │ s2 ref ────┼───→│ "World" String obj │
// │ list ref ───┼───→│ ArrayList object │
// └─────────────┘ │ ├─ elementData ref ──┼──→ [Object[10]]
// │ ├─ [Object[10]] ─────┼──→ [s1, s2, null, ...]
// └──────────────────────┘
Когда удаляем элемент
list.remove(0); // Удаляем "Hello"
// Что происходит:
// 1. Ссылка на "Hello" удаляется из массива
// 2. Остальные элементы сдвигаются влево
// 3. Последний элемент становится null
// Было: ["Hello", "World", null, null, ...]
// Стало: ["World", null, null, null, ...]
Сложность операций
// O(1) - добавление в конец (обычно)
list.add("item");
// O(n) - добавление в начало или середину (нужно сдвигать элементы)
list.add(0, "item");
// O(1) - получение элемента по индексу
String item = list.get(0);
// O(n) - удаление элемента (нужно сдвигать)
list.remove(0);
// O(n) - поиск элемента
int index = list.indexOf("item");
Сравнение с LinkedList
// ArrayList - ссылки в массиве
List<String> list = new ArrayList<>(); // O(1) доступ по индексу
// LinkedList - ссылки в узлах двусвязного списка
List<String> linked = new LinkedList<>(); // O(n) доступ по индексу
// Вставка в начало:
list.add(0, "item"); // O(n) - нужно сдвигать весь массив
linked.addFirst("item"); // O(1) - просто меняем ссылки
Практическая демонстрация
public class ArrayListDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// Добавляем элементы
for (int i = 0; i < 20; i++) {
list.add(i);
}
// Каждый элемент хранится как ссылка на объект Integer
Integer first = list.get(0); // Получает ссылку из массива
// Удаляем элемент
list.remove(5); // Сдвигает ссылки в массиве
// Проверяем
System.out.println(list.size()); // 19
}
}
Итог
Ссылки в ArrayList хранятся в обычном примитивном массиве Java (Object[]), который находится в Heap памяти. При добавлении элементов ArrayList автоматически увеличивает размер массива, копируя ссылки в новый, больший массив. Это обеспечивает быстрый доступ по индексу O(1), но медленные вставки и удаления в начало/середину O(n).