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

Какие коллекции использовал чаще всего

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

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

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

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

Коллекции в Java: Практическое применение

Обзор Collection Framework

Java Collection Framework предоставляет набор классов для хранения и манипуляции группами объектов. Разделяется на три основные категории:

Collection
├── List (упорядоченные, дубликаты разрешены)
│   ├── ArrayList
│   ├── LinkedList
│   └── CopyOnWriteArrayList
├── Set (уникальные значения)
│   ├── HashSet
│   ├── TreeSet
│   └── LinkedHashSet
└── Queue (очереди)
    ├── PriorityQueue
    └── Deque

Map (ключ-значение)
├── HashMap
├── TreeMap
├── LinkedHashMap
├── ConcurrentHashMap
└── WeakHashMap

1. ArrayList (Используется ПОСТОЯННО)

Описание: динамический массив, изменяемого размера. Самая частая коллекция.

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

  • Нужен быстрый доступ по индексу O(1)
  • Добавление/удаление в конце списка
  • Не критична многопоточность
// Чаще всего в real-world коде
List<User> users = new ArrayList<>();
users.add(new User("Alice"));
users.add(new User("Bob"));

// Итерирование
for (User user : users) {
    System.out.println(user.getName());
}

// Stream API (modern Java)
users.stream()
    .filter(u -> u.getAge() > 18)
    .map(User::getName)
    .forEach(System.out::println);

// Производительность:
// get(index) - O(1)
// add(element) - O(1) amortized
// remove(index) - O(n)
// contains(element) - O(n)

Реальный пример из моего опыта:

@Service
public class OrderService {
    public List<OrderDTO> getActiveOrders() {
        List<Order> orders = orderRepository.findByStatus(OrderStatus.ACTIVE);
        return orders.stream()
            .map(this::toDTO)
            .toList();  // Java 16+
    }
}

2. HashMap (Вторая по частоте)

Описание: реализация интерфейса Map на основе хеш-таблицы. Очень часто.

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

  • Быстрый поиск по ключу O(1)
  • Сохранение пар ключ-значение
  • Кэширование данных
  • Не критична порядок элементов
// Кэширование результатов
Map<Long, User> userCache = new HashMap<>();
userCache.put(1L, new User("Alice"));
userCache.put(2L, new User("Bob"));

User user = userCache.getOrDefault(1L, new User("Unknown"));

// Подсчёт частоты элементов
Map<String, Integer> wordCount = new HashMap<>();
for (String word : words) {
    wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}

// Производительность:
// get(key) - O(1) average
// put(key, value) - O(1) average
// remove(key) - O(1) average
// containsKey(key) - O(1) average

Реальный пример:

@Service
public class ProductService {
    private Map<Long, ProductDTO> productCache = new HashMap<>();
    
    public ProductDTO getProduct(Long id) {
        return productCache.computeIfAbsent(id, key -> 
            productRepository.findById(key)
                .map(this::toDTO)
                .orElse(null)
        );
    }
}

3. HashSet (Частая для уникальности)

Описание: реализация Set на основе HashMap. Гарантирует уникальность элементов.

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

  • Нужны только уникальные значения
  • Быстрый поиск O(1)
  • Удаление дубликатов
  • Порядок не важен
// Удаление дубликатов
List<Integer> numbers = List.of(1, 2, 2, 3, 3, 3, 4);
Set<Integer> unique = new HashSet<>(numbers);
// Результат: {1, 2, 3, 4}

// Проверка наличия элемента
Set<String> allowedRoles = new HashSet<>(List.of("ADMIN", "USER", "GUEST"));
if (allowedRoles.contains(role)) {
    // ...
}

// Производительность:
// add(element) - O(1) average
// remove(element) - O(1) average
// contains(element) - O(1) average

4. LinkedList (Редко, но нужно знать)

Описание: двусвязный список. Редко используется в реальных проектах.

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

  • Частые вставки/удаления в начало/конец O(1)
  • Реализация Queue/Deque
  • НЕ нужен быстрый доступ по индексу
// Редко, но пример есть
Queue<Task> taskQueue = new LinkedList<>();
taskQueue.offer(new Task("task1"));
taskQueue.offer(new Task("task2"));
Task next = taskQueue.poll();  // Удалить первый

// Производительность:
// get(index) - O(n) !!! ПЛОХО
// add(element) - O(1) если в конец
// add(0, element) - O(1) если в начало

5. TreeMap / TreeSet (Для сортировки)

Описание: упорядоченные коллекции на основе красно-чёрного дерева.

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

  • Нужна сортировка элементов
  • Range queries (элементы в диапазоне)
  • Сложные поиски (floor, ceiling)
// Сортировка по ключам
Map<Integer, String> scores = new TreeMap<>();
scores.put(90, "Alice");
scores.put(85, "Bob");
scores.put(95, "Charlie");
// Итерирование в порядке ключей: 85, 90, 95

// Range query
NavigableMap<Integer, String> high = scores.tailMap(90);
// Результат: {90->Alice, 95->Charlie}

// Производительность:
// get(key) - O(log n)
// put(key, value) - O(log n)
// containsKey(key) - O(log n)

6. ConcurrentHashMap (Многопоточность)

Описание: потокобезопасная версия HashMap. Использую в многопоточных приложениях.

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

  • Многопоточная среда (микросервисы, web-приложения)
  • Высокая конкурентность
  • Не хочешь синхронизировать весь HashMap
// Правильно для многопоточности
Map<String, User> cache = new ConcurrentHashMap<>();
cache.putIfAbsent("user1", new User("Alice"));

// Compute операции (атомарные)
cache.computeIfPresent("user1", (k, v) -> {
    v.incrementLoginCount();
    return v;
});

// Почему ConcurrentHashMap лучше Collections.synchronizedMap()?
// - Не блокирует всю таблицу, только segment
// - Лучше производительность при высокой конкурентности

Сравнительная таблица

КоллекцияgetaddremoveПорядокПотокобезопасна
ArrayListO(1)O(1)*O(n)ВставкиНет
LinkedListO(n)O(1)O(1)*ВставкиНет
HashMapO(1)*O(1)*O(1)*НетНет
TreeMapO(log n)O(log n)O(log n)По ключамНет
HashSetO(1)*O(1)*O(1)*НетНет
ConcurrentHashMapO(1)*O(1)*O(1)*НетДа

*O(1) average, O(n) worst case

My Real-World Projects

// Проект: E-Commerce Platform
public class ShoppingCart {
    private Map<Long, CartItem> items = new LinkedHashMap<>();  // Порядок важен
    private Set<String> appliedCoupons = new HashSet<>();
    private List<Order> orderHistory = new ArrayList<>();  // Часто итерируем
    
    public void addItem(Long productId, CartItem item) {
        items.put(productId, item);
    }
    
    public double calculateTotal() {
        return items.values().stream()
            .mapToDouble(CartItem::getPrice)
            .sum();
    }
}

// Проект: Social Network
public class UserService {
    private Map<Long, User> userCache = new ConcurrentHashMap<>();
    private Map<Long, Set<Long>> userFollowers = new ConcurrentHashMap<>();
    
    public boolean isFollowing(Long userId, Long followeeId) {
        return userFollowers
            .getOrDefault(userId, new HashSet<>())
            .contains(followeeId);
    }
}

Best Practices

Делай так:

  • Используй интерфейсы (List, Map, Set), а не конкретные классы
  • ArrayList для 95% случаев когда нужен список
  • HashMap для 95% случаев когда нужна ассоциативность
  • ConcurrentHashMap в многопоточных приложениях
  • Stream API для трансформации коллекций

Не делай так:

  • Vector и Hashtable (deprecated)
  • Collections.synchronizedMap() вместо ConcurrentHashMap
  • LinkedList для случайного доступа по индексу
  • Забывать про capacity/load factor при инициализации больших коллекций

Заключение

Чисто из опыта: ArrayList + HashMap покрывают 90% реальных задач. Остальные коллекции нужны для специфических сценариев. Помни о производительности и многопоточности!

Какие коллекции использовал чаще всего | PrepBro