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

В чем разница между динамическим и статическим массивами?

1.2 Junior🔥 191 комментариев
#Основы Java

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

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

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

# Разница между статическим и динамическим массивами в Java

В Java есть статические массивы (обычные arrays) и динамические коллекции (List). Это фундаментальное различие, важное для разработчика.

Определение и назначение

Статический массив (Array)

// Объявление и инициализация
int[] numbers = new int[5]; // Размер = 5, зафиксирован
String[] names = {"Alice", "Bob", "Charlie"}; // Размер = 3

// Доступ по индексу
int first = numbers[0];
String name = names[1];

// Размер
int length = numbers.length; // 5

// Попытка добавить элемент вызовет исключение
// numbers[5] = 10; // ArrayIndexOutOfBoundsException!

Динамический массив (List)

// Объявление
List<Integer> numbers = new ArrayList<>();
List<String> names = new ArrayList<>();

// Добавление элементов (размер растёт автоматически)
numbers.add(1);
numbers.add(2);
numbers.add(3);

names.add("Alice");
names.add("Bob");

// Доступ
int first = numbers.get(0);
String name = names.get(1);

// Размер
int size = numbers.size(); // Динамический

Основные различия

АспектСтатический массивДинамический (List)
РазмерФиксированныйПеременный
Объявлениеint[] arr = new int[10]List<Integer> list = new ArrayList<>()
ДобавлениеНевозможно (только переприсваивание)list.add(value)
УдалениеТолько через новый массивlist.remove(index)
ПоискЛинейный поиск O(n)list.contains() или indexOf() O(n)
ПамятьВыделяется разомВыделяется при необходимости
ПроизводительностьОчень быстрый доступ O(1)Быстрый доступ O(1), но оверхед
ГенерикиНет (массив объектов)Есть (type-safe)
Null значенияЗависит от типаМожно хранить null

Производительность

Доступ (get)

// Оба O(1), но массив быстрее
int[] arr = new int[1000000];
List<Integer> list = new ArrayList<>(arr);

// Статический массив
int start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
    int x = arr[i];
}
long time1 = System.nanoTime() - start; // ~1ms

// Динамический список
start = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
    int x = list.get(i); // Микро-оверхед
}
long time2 = System.nanoTime() - start; // ~2ms

Добавление

// Массив: нужно создавать новый каждый раз
int[] arr = new int[5];
arr[0] = 1;
// Для добавления 6-го элемента:
int[] newArr = new int[6];
System.arraycopy(arr, 0, newArr, 0, 5);
newArr[5] = 6;
arr = newArr; // O(n) операция!

// Список: амортизированное O(1)
List<Integer> list = new ArrayList<>();
list.add(1); // O(1) амортизированная
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6); // При необходимости список удваивает размер

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

Используй статический массив когда:

// 1. Размер известен и фиксирован
public class ChessBoard {
    private int[][] board = new int[8][8]; // Шахматная доска всегда 8x8
}

// 2. Максимальная производительность критична
public class ImageProcessor {
    private byte[] pixels = new byte[1920 * 1080]; // Для обработки пиксели
    // Быстрый доступ к пикселям critical для performance
}

// 3. Совместимость с low-level API
public void handleBytes(byte[] buffer) {
    // Многие API требуют массивы
}

// 4. Примитивные типы (экономия памяти)
int[] primes = {2, 3, 5, 7, 11, 13}; // Очень эффективно по памяти

Используй динамический массив (List) когда:

// 1. Размер неизвестен заранее
List<User> users = new ArrayList<>();
while (scanner.hasNextLine()) {
    users.add(parseUser(scanner.nextLine())); // Неизвестно сколько будет
}

// 2. Нужны операции вставки/удаления
List<Integer> numbers = new ArrayList<>();
numbers.add(0, 100); // Вставить в начало
numbers.remove(0);    // Удалить

// 3. Нужна типизация (Generics)
List<String> names = new ArrayList<>(); // Type-safe
// String name = names.get(0); // Безопасный cast

// 4. Удобство использования
List<Integer> list = Arrays.asList(1, 2, 3);
list.stream()
    .filter(x -> x > 1)
    .map(x -> x * 2)
    .collect(Collectors.toList());

Практические примеры

Пример 1: Обработка изображения (статический массив)

public class ImageBuffer {
    private final byte[] pixels; // Фиксированный размер = width * height
    private final int width;
    private final int height;
    
    public ImageBuffer(int width, int height) {
        this.width = width;
        this.height = height;
        this.pixels = new byte[width * height];
    }
    
    public byte getPixel(int x, int y) {
        return pixels[y * width + x]; // O(1) доступ
    }
    
    public void setPixel(int x, int y, byte value) {
        pixels[y * width + x] = value; // O(1)
    }
}

// Использование
ImageBuffer img = new ImageBuffer(1920, 1080);
img.setPixel(100, 200, (byte) 255);

Пример 2: Коллекция записей (динамический массив)

public class EventLog {
    private List<Event> events = new ArrayList<>(); // Динамический
    
    public void logEvent(Event event) {
        events.add(event); // Добавляем без заботы о размере
    }
    
    public void removeOldEvents(LocalDateTime cutoff) {
        events.removeIf(e -> e.getTimestamp().isBefore(cutoff));
    }
    
    public List<Event> getRecentEvents(int count) {
        return events.stream()
            .skip(Math.max(0, events.size() - count))
            .collect(Collectors.toList());
    }
}

Пример 3: Преобразование между ними

// Массив → Список
int[] array = {1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(array); // Неизменяемый список!
// или
List<Integer> list2 = new ArrayList<>(Arrays.asList(array)); // Изменяемый

// Список → Массив
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
String[] array = names.toArray(new String[0]); // Преобразование

// Stream API
List<Integer> numbers = Arrays.stream(new int[]{1, 2, 3})
    .boxed()
    .collect(Collectors.toList());

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

public class ArrayList<E> {
    private Object[] elementData; // Внутренний статический массив!
    private int size; // Текущее количество элементов
    
    public void add(E e) {
        ensureCapacity(size + 1); // Проверяем место
        elementData[size++] = e;
    }
    
    private void ensureCapacity(int minCapacity) {
        if (minCapacity > elementData.length) {
            // Создаём новый массив большего размера
            int newCapacity = elementData.length + (elementData.length >> 1); // x1.5
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }
    
    public E get(int index) {
        return (E) elementData[index]; // Просто доступ к массиву
    }
}

// Внутренне ArrayList использует массив и растягивает его при необходимости

Сравнение операций

// Доступ: Массив O(1), ArrayList O(1)
int x = arr[5];
int y = list.get(5);

// Добавление в конец: Массив O(n), ArrayList O(1) амортиз.
arr = Arrays.copyOf(arr, arr.length + 1);
arr[arr.length - 1] = value;
list.add(value); // Быстрее!

// Вставка в середину: Оба O(n)
// Массив: перекопировать весь хвост
// ArrayList: то же самое внутренне

// Удаление: Оба O(n)
// Нужно сдвинуть все элементы после удаляемого

Итог

Статический массив:

  • Фиксированный размер
  • Очень быстрый доступ
  • Эффективная память (примитивы)
  • Когда размер известен и не меняется

Динамический (List):

  • Переменный размер
  • Удобные операции (add, remove)
  • Type-safe (Generics)
  • Когда размер неизвестен или часто меняется

Правило: Начни с List, если размер не критичен. Используй массив только если у тебя есть specific performance требования.