Что такое WeakHashMap?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# WeakHashMap
Определение
WeakHashMap — это реализация интерфейса Map в Java, которая хранит слабые ссылки (weak references) на ключи. Когда ключ больше не используется в приложении, он может быть удалён сборщиком мусора, и соответствующая запись будет автоматически удалена из карты.
Основное назначение
1. Проблема обычного HashMap
Обычный HashMap держит сильные ссылки на ключи:
Map<Integer, String> map = new HashMap<>();
Integer key = new Integer(1);
map.put(key, "value");
key = null; // Переменная key больше не ссылается на объект
// Но HashMap ВСЕГДА держит сильную ссылку на Integer(1)!
// Объект не будет удалён сборщиком мусора
// Это может привести к утечке памяти
2. Слабые ссылки (Weak References)
WeakHashMap использует слабые ссылки, которые НЕ препятствуют сборке мусора:
Map<Integer, String> map = new WeakHashMap<>();
Integer key = new Integer(1);
map.put(key, "value");
System.out.println(map.size()); // 1
key = null; // Переменная освобождена
System.gc(); // Сборка мусора
System.out.println(map.size()); // 0 — ключ удалён автоматически!
3. Типы ссылок в Java
Strong Reference (Сильная ссылка)
└─ Object остаётся в памяти пока есть сильная ссылка
Weak Reference (Слабая ссылка)
└─ Object может быть удалён сборщиком мусора если нет сильных ссылок
Soft Reference (Мягкая ссылка)
└─ Object удаляется только при нехватке памяти
Phantom Reference (Фантомная ссылка)
└─ Самая слабая, используется для финализации
Пример всех типов ссылок:
String str = new String("hello"); // Strong reference
WeakReference<String> weak = new WeakReference<>(str);
SoftReference<String> soft = new SoftReference<>(str);
PhantomReference<String> phantom = new PhantomReference<>(str, queue);
str = null; // Удалили strong reference
System.gc();
// weak станет null первой
if (weak.get() == null) {
System.out.println("Weak reference: объект удалён");
}
// soft может остаться в памяти при наличии места
if (soft.get() != null) {
System.out.println("Soft reference: объект ещё в памяти");
}
4. Практические примеры использования WeakHashMap
Пример 1: Кэш с автоматической очисткой
public class ImageCache {
private static final Map<String, byte[]> cache = new WeakHashMap<>();
public void cacheImage(String filename, byte[] imageData) {
cache.put(filename, imageData);
}
public byte[] getImage(String filename) {
return cache.get(filename);
}
// Нет необходимости вручную удалять из кэша
// Когда нет других ссылок на ключ, запись автоматически удалится
}
Пример 2: Слушатели событий (Listeners)
public class EventBus {
private Map<Object, List<EventListener>> listeners = new WeakHashMap<>();
public void subscribe(Object source, EventListener listener) {
listeners.computeIfAbsent(source, k -> new ArrayList<>()).add(listener);
}
public void publish(Object source, Event event) {
List<EventListener> eventListeners = listeners.get(source);
if (eventListeners != null) {
for (EventListener listener : eventListeners) {
listener.onEvent(event);
}
}
}
// Когда source больше не используется в коде,
// запись в listeners автоматически удалится
}
Пример 3: Кэш объектов DOM элементов
public class DOMCache {
private Map<Element, ElementData> elementData = new WeakHashMap<>();
public void setData(Element element, ElementData data) {
elementData.put(element, data);
}
public ElementData getData(Element element) {
return elementData.get(element);
}
// Когда DOM element удаляется со страницы,
// данные автоматически удаляются из кэша
}
5. Сравнение HashMap и WeakHashMap
| Параметр | HashMap | WeakHashMap |
|---|---|---|
| Тип ссылок | Сильные | Слабые |
| Сборка мусора | Не удаляет ключи | Удаляет неиспользуемые ключи |
| Используемость | Универсальная | Специализированная |
| Производительность | Быстрее | Немного медленнее |
| Утечки памяти | Вероятны при неправильном использовании | Автоматическая очистка |
| Потокобезопасность | Нет (используй Collections.synchronizedMap) | Нет |
6. Потокобезопасность
WeakHashMap НЕ потокобезопасен, как и HashMap:
// Неправильно для многопоточности
Map<Key, Value> map = new WeakHashMap<>();
// Правильно
Map<Key, Value> map = Collections.synchronizedMap(new WeakHashMap<>());
7. Важные особенности
Непредсказуемость:
WeakHashMap<String, String> map = new WeakHashMap<>();
map.put("key", "value");
// Строки в Java интернируются, поэтому строка живёт долго
System.out.println(map.size()); // Может быть 1 или 0
// С объектами это яснее
weakMap.put(new Object(), "value");
System.out.println(weakMap.size()); // 0 (объект был удалён)
Проблема с примитивами:
// Integer с малыми значениями кэшируются
Integer key1 = 5; // Автоматически кэшированная ссылка
Map<Integer, String> map = new WeakHashMap<>();
map.put(key1, "value");
key1 = null;
System.gc();
System.out.println(map.size()); // Может быть 1 (Integer 5 живёт в кэше)
// Integer с большими значениями
Integer key2 = 200;
map.put(key2, "value");
key2 = null;
System.gc();
System.out.println(map.size()); // 0 (удалится как ожидается)
8. Когда НЕ использовать WeakHashMap
// Неправильно — строки интернируются в Java
Map<String, String> cache = new WeakHashMap<>();
// Неправильно — Integer кэшируются для значений -128..127
Map<Integer, String> cache = new WeakHashMap<>();
// Правильно — пользовательские объекты
Map<User, UserData> cache = new WeakHashMap<>();
Выводы
WeakHashMap — это специализированный инструмент для:
- Кэширования с автоматической очисткой
- Хранения метаданных, связанных с объектами
- Реализации слушателей событий
- Предотвращения утечек памяти в специфических сценариях
Однако его нужно использовать с осторожностью, так как поведение непредсказуемо с интернированными строками и кэшированными числами. В современных приложениях часто лучше использовать явное управление кэшем через Caffeine или Guava Cache.