← Назад к вопросам
В чем разница между динамическим и статическим массивами?
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 требования.