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

Что такое процедура в SQL?

2.0 Middle🔥 221 комментариев
#Базы данных (SQL)

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

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

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

ArrayList: реализация на основе динамического массива

Основная структура

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

Внутреннее устройство:

public class ArrayList<E> extends AbstractList<E> implements List<E> {
    // Основной массив для хранения элементов
    private Object[] elementData;
    
    // Текущее количество элементов
    private int size;
    
    // Стандартная начальная ёмкость
    private static final int DEFAULT_CAPACITY = 10;
    
    // Пустой массив для экземпляра с нулевой ёмкостью
    private static final Object[] EMPTY_ELEMENTDATA = {};
}

Как работает динамическое расширение

1. Инициализация:

public ArrayList() {
    // Начинаем с пустого массива
    this.elementData = EMPTY_ELEMENTDATA;
}

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

2. Добавление элемента:

public boolean add(E e) {
    // Убедиться что достаточно места
    ensureCapacityInternal(size + 1);
    // Добавить элемент в конец
    elementData[size++] = e;
    return true;
}

3. Расширение массива при нехватке места:

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == EMPTY_ELEMENTDATA) {
        // Первое добавление: выделить DEFAULT_CAPACITY
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    // Нужно расширение если требуется больше чем есть
    if (minCapacity - elementData.length > 0) {
        grow(minCapacity);
    }
}

private void grow(int minCapacity) {
    // Старая ёмкость
    int oldCapacity = elementData.length;
    
    // Новая ёмкость = старая + (старая / 2)
    // Увеличиваем на 50%
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // Если даже это не достаточно
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    
    // Защита от переполнения
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        newCapacity = hugeCapacity(minCapacity);
    }
    
    // Копируем старый массив в новый
    elementData = Arrays.copyOf(elementData, newCapacity);
}

Пример работы

ArrayList<String> list = new ArrayList<>();

// Начальное состояние
// elementData = [] (пусто)
// size = 0
// capacity = 0

list.add("item1");
// Первое добавление:
//   Вызывается grow(1)
//   newCapacity = Math.max(10, 1) = 10
//   elementData = new Object[10]
//   elementData[0] = "item1"
//   size = 1

list.add("item2");
list.add("item3");
// ... item4-10 добавляются без расширения
// size = 10, capacity = 10

list.add("item11");
// Расширение:
//   oldCapacity = 10
//   newCapacity = 10 + 10/2 = 15
//   elementData = Arrays.copyOf(elementData, 15)
//   elementData[10] = "item11"
//   size = 11, capacity = 15

Операции с временной сложностью

// O(1) - добавление в конец (в среднем)
list.add("item");

// O(1) - получение по индексу
String item = list.get(5);

// O(n) - добавление в середину
list.add(5, "new item");  // сдвигаются все элементы после позиции 5

// O(n) - удаление из середины
list.remove(5);  // сдвигаются все элементы

// O(n) - поиск элемента
int index = list.indexOf("item");  // проходит весь список

Визуализация операций:

Добавление в конец: O(1)
[A][B][C][ ][ ]  <- есть место
[A][B][C][D][ ] <- просто добавляем

Добавление в середину: O(n)
[A][B][C][D][ ]
[A][X][B][C][D] <- сдвигаем всё после позиции

Удаление из середины: O(n)
[A][X][B][C][D]
[A][B][C][D][ ] <- сдвигаем всё влево

Особенности реализации

1. Использование Object[]:

Элементы хранятся в массиве Object[], а не прямо в массиве типа E.

// Внутри
private Object[] elementData;

// При добавлении
elementData[size++] = e;  // Object

// При получении требуется cast
@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];  // кастуем обратно
}

2. Быстрое расширение:

Для снижения количества переаллокаций используется 50% рост, а не удвоение:

10 элементов -> 15 (10 + 5)
15 элементов -> 22 (15 + 7)
22 элемента -> 33 (22 + 11)
...

Это баланс между памятью и частотой расширений

3. Итератор:

public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    int cursor;  // индекс следующего элемента
    int lastRet = -1;  // индекс последнего возвращённого
    int expectedModCount = modCount;
    
    public E next() {
        checkForComodification();
        int i = cursor;
        // ... проверки границ
        cursor = i + 1;
        return get(lastRet = i);
    }
    
    // Защита от ConcurrentModificationException
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

Сравнение с другими структурами

СтруктураДоступВставкаУдалениеПамять
ArrayListO(1)O(n)O(n)много
LinkedListO(n)O(1)O(1)мало
ArrayO(1)--фиксированная

Когда ArrayList расширяется

ArrayList<Integer> list = new ArrayList<>(5);
// capacity = 5

for (int i = 0; i < 100; i++) {
    list.add(i);
    // Расширения происходят при:
    // size = 5 -> capacity = 7
    // size = 7 -> capacity = 10
    // size = 10 -> capacity = 15
    // size = 15 -> capacity = 22
    // ...
}

Оптимизация

Предвыделение памяти если знаешь размер:

// Плохо: много расширений
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    list.add("item" + i);  // много расширений
}

// Хорошо: одно выделение
List<String> list = new ArrayList<>(10000);
for (int i = 0; i < 10000; i++) {
    list.add("item" + i);  // нет расширений
}

Вывод

ArrayList реализован на основе динамического массива:

  • Структура: Object[] массив с двумя полями (elementData, size)
  • Расширение: увеличивается на 50% когда нужно больше места
  • Копирование: System.arraycopy() используется для переноса элементов
  • Сложность: O(1) доступ, O(n) вставка/удаление в середину
  • Оптимизация: предвыдели память если знаешь примерный размер

Это причина почему ArrayList отлично подходит для частого чтения и добавления в конец, но плохо для частых вставок в середину.