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

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

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

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

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

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

Разница между массивами и коллекциями в Java

Краткая таблица

┌──────────────┬─────────────────┬──────────────────┐
│ Параметр     │ Массивы          │ Коллекции         │
├──────────────┼─────────────────┼──────────────────┤
│ Размер       │ Фиксированный    │ Динамический      │
│ Тип          │ Примитивы + объ. │ Только объекты    │
│ Производ.    │ Быстрее          │ Медленнее          │
│ Память       │ Меньше           │ Больше            │
│ Удобство     │ Низкое           │ Высокое           │
│ Типизация    │ Слаба            │ Сильная (Generics)│
└──────────────┴─────────────────┴──────────────────┘

1. Размер и гибкость

Массивы — фиксированный размер:

// Размер указывается при создании
int[] array = new int[5];  // Ровно 5 элементов
array[0] = 10;
array[1] = 20;
// array[5] = 30;  // ❌ ArrayIndexOutOfBoundsException!

// Если нужно больше — переписать весь массив
int[] newArray = new int[10];  // Новый массив
System.arraycopy(array, 0, newArray, 0, array.length);
array = newArray;  // Переприсвоение

Коллекции — динамический размер:

List<Integer> list = new ArrayList<>();  // Начинает с capacity 10
list.add(10);
list.add(20);
list.add(30);
// ... можно добавлять без ограничений
list.add(1_000_000);  // Нет проблем, автоматически расширяется

// Удаление тоже просто
list.remove(0);  // Удалить первый элемент
list.remove(Integer.valueOf(20));  // Удалить значение

2. Типизация

Массивы:

// Хранит только нужный тип
int[] numbers = {1, 2, 3};
// numbers[0] = "string";  // ❌ Compile error

// Object[] может хранить что угодно (опасно!)
Object[] objects = new Object[3];
objects[0] = "string";
objects[1] = 42;
objects[2] = new Date();

// При использовании нужно кастить
String str = (String) objects[0];  // Cast

Коллекции — типизация через Generics:

// Строго типизировано
List<String> strings = new ArrayList<>();
strings.add("hello");
// strings.add(42);  // ❌ Compile error!

List<Integer> numbers = new ArrayList<>();
numbers.add(42);
// numbers.add("string");  // ❌ Compile error!

// Автоматический unboxing
Integer value = numbers.get(0);
int primitive = numbers.get(0);  // ✅ Автоматически распаковывается

3. Примитивные типы

Массивы поддерживают примитивы:

// Примитивы работают напрямую
int[] ints = {1, 2, 3};
double[] doubles = {1.5, 2.5, 3.5};
boolean[] flags = {true, false, true};
char[] chars = {'a', 'b', 'c'};

// Нет overhead'а на упаковку

Коллекции требуют объектных обёрток:

// ❌ Нельзя: List<int> — синтаксис ошибка
// List<int> ints = new ArrayList<>();  // ✗

// Нужно использовать wrapper классы
List<Integer> ints = new ArrayList<>();  // Integer — обёртка для int
ints.add(10);  // Автоматический boxing: 10 → Integer(10)
int value = ints.get(0);  // Автоматический unboxing: Integer(10) → 10

// Есть overhead на создание объектов

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

Массивы — быстрее:

// Прямой доступ к элементам (O(1))
long start = System.nanoTime();
int[] array = new int[1_000_000];
for (int i = 0; i < array.length; i++) {
    array[i] = i;
}
long elapsed = System.nanoTime() - start;
System.out.println("Массив: " + elapsed + " ns");
// ~10ms

Коллекции — медленнее:

long start = System.nanoTime();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
    list.add(i);  // Может пересоздаваться (resize)
}
long elapsed = System.nanoTime() - start;
System.out.println("List: " + elapsed + " ns");
// ~30ms (из-за boxing, resize'а, и т.д.)

Но для small datasets разницы почти нет.

5. Использование памяти

Массивы — компактнее:

int[] array = new int[1000];
// Память: примерно 4 KB (1000 * 4 bytes)

Коллекции — больше памяти:

List<Integer> list = new ArrayList<>(1000);
// Память: ~40 KB (каждый Integer объект + overhead)
// Потому что Integer имеет header, padding, итд

6. Удобство работы

Массивы — минимум удобства:

int[] array = {1, 2, 3, 4, 5};

// Нет встроенных методов
// Нельзя добавить элемент (нужно копировать)
// Нельзя удалить элемент
// Нельзя поискать elemento легко
int index = -1;
for (int i = 0; i < array.length; i++) {
    if (array[i] == 3) {
        index = i;
        break;
    }
}

Коллекции — очень удобны:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

// Встроенные методы
list.add(6);  // Добавить
list.remove(0);  // Удалить
list.contains(3);  // Проверить наличие
int index = list.indexOf(3);  // Найти индекс

// Функциональный стиль (Java 8+)
list.stream()
    .filter(n -> n > 2)
    .forEach(System.out::println);

// Итерация
for (int n : list) { System.out.println(n); }  // for-each

7. Типы коллекций

List — упорядоченная коллекция:

List<String> list = new ArrayList<>();  // Быстрое добавление в конец
List<String> list2 = new LinkedList<>();  // Быстрое добавление/удаление в начало

list.add("first");
list.add("second");
String first = list.get(0);

Set — уникальные значения:

Set<Integer> set = new HashSet<>();  // Быстрый поиск
Set<Integer> set2 = new TreeSet<>();  // Упорядоченный

set.add(1);
set.add(1);  // Не добавится (дублик)
set.contains(1);  // true

Map — ключ-значение:

Map<String, Integer> map = new HashMap<>();
map.put("age", 30);
map.put("year", 2024);

int age = map.get("age");

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

Используй массивы:

  • Нужна максимальная производительность
  • Размер известен и не меняется
  • Работа с примитивными типами
  • Многомерные данные (matrix[row][col])
int[][] matrix = new int[10][10];  // 2D массив
for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        matrix[i][j] = i * j;
    }
}

Используй коллекции:

  • Размер неизвестен или меняется
  • Нужны удобные методы (add, remove, contains)
  • Нужна типизация через Generics
  • Нужна гибкость
List<String> names = new ArrayList<>();
names.add("John");
names.add("Jane");
names.remove(0);
if (names.contains("John")) { ... }

9. Преобразование между ними

// Массив → Список
int[] array = {1, 2, 3};
List<Integer> list = Arrays.stream(array)
    .boxed()
    .collect(Collectors.toList());

// Список → Массив
List<String> list2 = Arrays.asList("a", "b", "c");
String[] array2 = list2.toArray(new String[0]);

// Или просто конвертировать
Integer[] wrapperArray = list.toArray(new Integer[0]);

10. Примеры из реальной жизни

Когда код использует массивы:

// Вычисления (matrix operations, FFT, итд)
public double[] convolution(double[] signal, double[] kernel) {
    double[] result = new double[signal.length];
    // ...
    return result;
}

// Низкоуровневая работа (байтовые потоки)
public byte[] readFile(String path) {
    byte[] buffer = new byte[8192];
    // ...
    return buffer;
}

Когда код использует коллекции:

// Spring Data JPA
List<User> users = userRepository.findAll();

// Rest API
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("data", users);

// Обработка данных
Set<String> uniqueTags = article.getTags()
    .stream()
    .map(Tag::getName)
    .collect(Collectors.toSet());

Вывод

Массивы — это низкоуровневая, быстрая, но менее удобная структура. Коллекции — это высокоуровневые, гибкие, удобные контейнеры. В 99% случаев современного кода ты будешь использовать коллекции. Массивы используются редко, в основном для оптимизаций и специфичных задач.