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

Что будет при вызове clear к ArrayList?

1.7 Middle🔥 61 комментариев
#Основы Java

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

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

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

Метод clear() в ArrayList

Это отличный вопрос, который проверяет понимание внутреннего устройства ArrayList и управления памятью в Java. Давайте разберемся, что именно происходит при вызове clear().

Что делает clear()

Метод clear() в ArrayList удаляет все элементы из коллекции, но не освобождает выделенную памяти. Внутренний массив остается тем же размером.

public class ClearExample {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        
        // Добавляем элементы
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        list.add("E");
        
        System.out.println("Size before clear: " + list.size());        // 5
        System.out.println("Capacity before clear: " + getCapacity(list)); // 10
        
        // Вызываем clear()
        list.clear();
        
        System.out.println("Size after clear: " + list.size());        // 0
        System.out.println("Capacity after clear: " + getCapacity(list)); // 10 (не изменилось!)
        System.out.println("isEmpty: " + list.isEmpty());              // true
    }
}

Внутреннее устройство clear()

Исходный код ArrayList.clear():

public class ArrayList<E> {
    transient Object[] elementData; // Внутренний массив
    private int size; // Текущее количество элементов
    
    public void clear() {
        // Обнуляем все ссылки
        for (int i = 0; i < size; i++) {
            elementData[i] = null;  // ВАЖНО: Обнуляем ссылки!
        }
        
        // Устанавливаем size в 0
        size = 0;
    }
}

Визуально это выглядит так:

ДО clear():

elementData: ["A", "B", "C", "D", "E", null, null, null, null, null]
size: 5
indices:  0    1    2    3    4    5     6     7     8     9

ПОСЛЕ clear():

elementData: [null, null, null, null, null, null, null, null, null, null]
size: 0
indices:  0      1      2      3      4      5      6      7      8      9

Почему обнуляются ссылки?

Это критически важно для garbage collection:

public class GarbageCollectionExample {
    public static void main(String[] args) {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("John", 1000000)); // Большой объект
        users.add(new User("Jane", 2000000)); // Большой объект
        
        // БЕЗ обнуления ссылок (как было в старых версиях)
        // Объект User остается в памяти, даже если list.clear()
        // потому что на него есть ссылка в elementData[0]
        
        users.clear();
        
        // С обнулением ссылок (как в современной ArrayList)
        // Объекты User становятся недостижимыми и будут собраны GC
    }
}

Важный момент: Емкость остается неизменной

public class CapacityExample {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        
        // Добавляем много элементов
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        
        int capacityBefore = getCapacity(list); // ~1500 (внутренний массив)
        System.out.println("Capacity before: " + capacityBefore);
        System.out.println("Size before: " + list.size());           // 1000
        System.out.println("Memory used: ~" + (capacityBefore * 8) + " bytes");
        
        list.clear(); // Только обнуляет элементы и size
        
        int capacityAfter = getCapacity(list); // Все еще ~1500!
        System.out.println("\nCapacity after: " + capacityAfter);
        System.out.println("Size after: " + list.size());            // 0
        System.out.println("Memory still used: ~" + (capacityAfter * 8) + " bytes");
        
        // Сравнение
        System.out.println("\nCapacity изменилось? " + (capacityBefore == capacityAfter)); // true
    }
}

Если нужна реальная очистка памяти

Вариант 1: Переустановить ArrayList

public class MemoryCleanup1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            list.add("Item " + i);
        }
        
        // НЕПРАВИЛЬНО: clear() не освобождает память
        list.clear();
        // list все еще держит в памяти массив size 100000+
        
        // ПРАВИЛЬНО: Переустановить список
        list = new ArrayList<>(); // Старый список для GC
    }
}

Вариант 2: Использовать trimToSize()

public class MemoryCleanup2 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        
        // Добавляем элементы
        for (int i = 0; i < 1000; i++) {
            list.add("Item " + i);
        }
        
        System.out.println("Capacity: " + getCapacity(list)); // 1500+
        
        // Очищаем
        list.clear();
        
        // Сокращаем емкость до размера (0)
        list.trimToSize();
        
        System.out.println("Capacity after trim: " + getCapacity(list)); // 0
        System.out.println("Memory освобождена!");
    }
}

Сравнение different методов очистки

МетодSizeCapacityПамятьИспользование
clear()0Без измененийОсталасьКогда план добавлять элементы
clear() + trimToSize()00ОсвобожденаКогда больше не нужны элементы
list = null--ОсвобожденаКогда переменная больше не нужна
list = new ArrayList<>()010 (default)ОсвобожденаПереиспользование списка

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

public class ClearPractice {
    
    // Сценарий 1: Переиспользуем список
    public void processBatches() {
        ArrayList<String> batch = new ArrayList<>();
        
        for (int i = 0; i < 100; i++) {
            // Добавляем элементы
            addItems(batch);
            
            // Обрабатываем
            processBatch(batch);
            
            // Очищаем для следующей итерации
            // clear() здесь идеален: size обнуляется, capacity остается
            batch.clear();
        }
    }
    
    // Сценарий 2: Большая коллекция, нужно освободить память
    public void loadLargeDataset() {
        ArrayList<User> users = new ArrayList<>();
        loadMillionUsers(users);
        
        processUsers(users);
        
        // Нужно освободить память
        users.clear();
        users.trimToSize(); // ИЛИ users = null;
    }
    
    // Сценарий 3: Проверка пустоты
    public void checkEmpty() {
        ArrayList<String> list = new ArrayList<>();
        list.add("A");
        list.clear();
        
        if (list.isEmpty()) {  // true - безопасно проверять
            System.out.println("List пуст");
        }
    }
}

Важные моменты

1. Thread safety:

public class ThreadSafety {
    // clear() НЕ потокобезопасен
    ArrayList<String> list = new ArrayList<>();
    
    // В многопоточной среде нужен синхронизированный список
    List<String> syncList = Collections.synchronizedList(new ArrayList<>());
    syncList.clear(); // Более безопасно
    
    // ИЛИ явная синхронизация
    synchronized(list) {
        list.clear();
    }
}

2. Поведение со ссылками:

public class ReferenceBehavior {
    public static void main(String[] args) {
        ArrayList<List<String>> lists = new ArrayList<>();
        List<String> innerList = new ArrayList<>();
        
        innerList.add("A");
        lists.add(innerList);
        
        lists.clear(); // Обнуляет ссылку на innerList в ArrayList
        
        // Но innerList все еще существует!
        innerList.add("B"); // OK
        System.out.println(innerList); // [A, B]
    }
}

Резюме

При вызове clear() на ArrayList происходит:

  1. Обнуляются все ссылки в массиве elementData (критично для GC)
  2. size устанавливается в 0 (list.isEmpty() вернет true)
  3. Емкость (capacity) остается неизменной (объект не освобождает выделенный массив)
  4. Объекты становятся доступны для GC (нет ссылок)
  5. Объект ArrayList остается готов к переиспользованию (можно добавлять новые элементы)

Это очень эффективно для переиспользования ArrayList, но если нужно реально освободить память, используйте trimToSize() или переустановите список.