← Назад к вопросам
Как поменять тип коллекции
2.2 Middle🔥 201 комментариев
#Stream API и функциональное программирование#Коллекции
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как поменять тип коллекции
В Java часто требуется конвертировать данные между разными типами коллекций (List, Set, Map, Queue). Это критическая задача при работе с данными. Рассмотрю все способы преобразования коллекций.
1. Основные типы коллекций и их отношения
// Иерархия коллекций
Collection
├── List (упорядочена, может быть дубликатов)
│ ├── ArrayList
│ ├── LinkedList
│ └── CopyOnWriteArrayList
├── Set (без дубликатов, неупорядочена)
│ ├── HashSet
│ ├── LinkedHashSet
│ └── TreeSet
└── Queue (очередь, FIFO)
├── LinkedList
├── PriorityQueue
└── Deque
Map (ключ-значение, не наследует Collection)
├── HashMap
├── LinkedHashMap
├── TreeMap
└── ConcurrentHashMap
2. List ↔ Set преобразования
List → Set:
// Способ 1: Через конструктор (самый простой)
List<String> list = Arrays.asList("apple", "banana", "apple");
Set<String> set = new HashSet<>(list);
// Результат: {apple, banana} - дубликаты удалены
// Способ 2: LinkedHashSet (сохраняет порядок)
Set<String> linkedSet = new LinkedHashSet<>(list);
// Результат: {apple, banana} - в том же порядке
// Способ 3: TreeSet (сортирует)
Set<String> treeSet = new TreeSet<>(list);
// Результат: {apple, banana} - отсортировано
// Способ 4: Stream API
Set<String> streamSet = list.stream()
.collect(Collectors.toSet()); // HashSet
Set<String> linkedStreamSet = list.stream()
.collect(Collectors.toCollection(LinkedHashSet::new));
Set → List:
Set<String> set = new HashSet<>(Arrays.asList("apple", "banana"));
// Способ 1: Через конструктор
List<String> list = new ArrayList<>(set);
// Способ 2: Stream API
List<String> streamList = set.stream()
.collect(Collectors.toList());
// Способ 3: addAll
List<String> newList = new ArrayList<>();
newList.addAll(set);
// Способ 4: Сортированный List
List<String> sortedList = set.stream()
.sorted()
.collect(Collectors.toList());
3. List ↔ Array преобразования
Array → List:
String[] array = {"apple", "banana", "cherry"};
// Способ 1: Arrays.asList (НЕ изменяемый список!)
List<String> list = Arrays.asList(array); // ⚠️ Fixed-size
list.add("date"); // UnsupportedOperationException!
// Способ 2: Новый изменяемый список
List<String> mutableList = new ArrayList<>(Arrays.asList(array));
mutableList.add("date"); // OK
// Способ 3: Stream API
List<String> streamList = Arrays.stream(array)
.collect(Collectors.toList());
// Способ 4: Для примитивных типов
int[] intArray = {1, 2, 3};
List<Integer> intList = Arrays.stream(intArray)
.boxed() // int → Integer
.collect(Collectors.toList());
List → Array:
List<String> list = Arrays.asList("apple", "banana", "cherry");
// Способ 1: toArray() - создаёт Object[]
Object[] objectArray = list.toArray(); // [apple, banana, cherry]
// Способ 2: Типизированный массив (правильный способ)
String[] stringArray = list.toArray(new String[0]);
// или новый синтаксис (Java 11+)
String[] stringArray = list.toArray(String[]::new);
// Способ 3: Stream API
String[] streamArray = list.stream()
.toArray(String[]::new);
// Способ 4: Для примитивных типов
List<Integer> intList = Arrays.asList(1, 2, 3);
int[] intArray = intList.stream()
.mapToInt(Integer::intValue) // Integer → int
.toArray();
4. List ↔ Map преобразования
List → Map:
// Способ 1: Используя индекс как ключ
List<String> list = Arrays.asList("apple", "banana", "cherry");
Map<Integer, String> indexMap = new HashMap<>();
for (int i = 0; i < list.size(); i++) {
indexMap.put(i, list.get(i));
}
// Результат: {0=apple, 1=banana, 2=cherry}
// Способ 2: Stream API с индексом
Map<Integer, String> streamIndexMap = IntStream.range(0, list.size())
.boxed()
.collect(Collectors.toMap(
Function.identity(), // индекс как ключ
list::get // значение из списка
));
// Способ 3: Используя элемент как ключ
List<User> users = Arrays.asList(
new User(1, "John"),
new User(2, "Jane")
);
Map<Integer, User> userMap = users.stream()
.collect(Collectors.toMap(
User::getId, // ключ
Function.identity() // значение
));
// Способ 4: Элемент как ключ и значение
List<String> words = Arrays.asList("apple", "banana");
Map<String, Integer> lengthMap = words.stream()
.collect(Collectors.toMap(
Function.identity(), // ключ - само слово
String::length // значение - длина
));
// Результат: {apple=5, banana=6}
Map → List:
Map<Integer, String> map = new HashMap<>();
map.put(1, "apple");
map.put(2, "banana");
// Способ 1: Список ключей
List<Integer> keys = new ArrayList<>(map.keySet());
// Способ 2: Список значений
List<String> values = new ArrayList<>(map.values());
// Способ 3: Список пар Entry
List<Map.Entry<Integer, String>> entries =
new ArrayList<>(map.entrySet());
// Способ 4: Stream API - преобразование значений
List<String> uppercaseValues = map.values().stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// Способ 5: Преобразование в объекты
List<Pair<Integer, String>> pairs = map.entrySet().stream()
.map(e -> new Pair<>(e.getKey(), e.getValue()))
.collect(Collectors.toList());
5. Set ↔ Map преобразования
Set → Map:
Set<String> set = new HashSet<>(Arrays.asList("apple", "banana"));
// Способ 1: Элемент как ключ и значение
Map<String, String> map = set.stream()
.collect(Collectors.toMap(
Function.identity(), // ключ
Function.identity() // значение
));
// Способ 2: Элемент как ключ, длину как значение
Map<String, Integer> lengthMap = set.stream()
.collect(Collectors.toMap(
Function.identity(),
String::length
));
Map → Set:
Map<Integer, String> map = new HashMap<>();
map.put(1, "apple");
map.put(2, "banana");
// Способ 1: Set ключей
Set<Integer> keySet = map.keySet();
// Способ 2: Set значений (если есть дубликаты)
Set<String> valueSet = new HashSet<>(map.values());
// Способ 3: Set Entry объектов
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
6. Queue/Deque преобразования
List → Queue:
List<String> list = Arrays.asList("apple", "banana", "cherry");
// Способ 1: LinkedList (реализует Queue)
Queue<String> queue = new LinkedList<>(list);
// Способ 2: PriorityQueue (с сортировкой)
Queue<String> priorityQueue = new PriorityQueue<>(list);
// Способ 3: Deque (двусторонняя очередь)
Deque<String> deque = new LinkedList<>(list);
Queue → List:
Queue<String> queue = new LinkedList<>(Arrays.asList("apple", "banana"));
// Способ 1: Прямое преобразование
List<String> list = new ArrayList<>(queue);
// Способ 2: Stream API
List<String> streamList = queue.stream()
.collect(Collectors.toList());
// ⚠️ Важно: очередь может быть изменена при преобразовании
// Используйте новый список перед удалением из очереди
7. Сложные преобразования с фильтрацией
Преобразование с фильтром:
List<String> list = Arrays.asList("apple", "banana", "apricot", "cherry");
// List с фильтром
List<String> filtered = list.stream()
.filter(s -> s.startsWith("a")) // Только слова на 'a'
.collect(Collectors.toList());
// Результат: [apple, apricot]
// Set с фильтром
Set<String> filteredSet = list.stream()
.filter(s -> s.length() > 5)
.collect(Collectors.toSet());
// Результат: {banana, cherry, apricot}
// Map с фильтром
Map<String, Integer> filteredMap = list.stream()
.filter(s -> s.length() > 5)
.collect(Collectors.toMap(
Function.identity(),
String::length
));
8. Группировка при преобразовании
List → Map с группировкой:
List<User> users = Arrays.asList(
new User(1, "John", "IT"),
new User(2, "Jane", "HR"),
new User(3, "Bob", "IT")
);
// Группируем по отделу
Map<String, List<User>> byDepartment = users.stream()
.collect(Collectors.groupingBy(User::getDepartment));
// Результат: {
// IT: [User(1), User(3)],
// HR: [User(2)]
// }
// Группируем, но берём только имена
Map<String, List<String>> namesByDepartment = users.stream()
.collect(Collectors.groupingBy(
User::getDepartment,
Collectors.mapping(User::getName, Collectors.toList())
));
// Результат: {
// IT: [John, Bob],
// HR: [Jane]
// }
// Группируем и считаем
Map<String, Long> countByDepartment = users.stream()
.collect(Collectors.groupingBy(
User::getDepartment,
Collectors.counting()
));
// Результат: {IT: 2, HR: 1}
9. Практические примеры
Преобразование DTO в Entity:
List<UserDTO> dtos = getUserDTOs();
// DTO → Entity и сохранить в Set
Set<User> entities = dtos.stream()
.map(dto -> new User(dto.getName(), dto.getEmail()))
.collect(Collectors.toSet());
Преобразование Entity в DTO:
List<User> users = userService.findAll();
// Entity → DTO
List<UserDTO> dtos = users.stream()
.map(u -> new UserDTO(u.getId(), u.getName(), u.getEmail()))
.collect(Collectors.toList());
// или
List<UserDTO> dtos = users.stream()
.map(UserDTO::fromEntity) // Статический метод
.collect(Collectors.toList());
Кеширование коллекции в Map:
List<Product> products = getProducts();
// Кешируем в Map по ID для быстрого доступа
Map<Long, Product> productCache = products.stream()
.collect(Collectors.toMap(
Product::getId,
Function.identity()
));
// Использование
Product product = productCache.get(123L);
10. Производительность и выбор коллекции
Сложность операций:
List (ArrayList):
get(index): O(1)
add(end): O(1) amortized
add(middle): O(n)
remove: O(n)
Set (HashSet):
add: O(1)
remove: O(1)
contains: O(1)
Map (HashMap):
get: O(1)
put: O(1)
remove: O(1)
Queue (LinkedList):
offer/poll: O(1)
peek: O(1)
Чеклист преобразований
// Выбор правильного способа
// Нужен неизменяемый результат?
List<String> unmodifiable = Collections.unmodifiableList(list);
Set<String> unmodifiableSet = Collections.unmodifiableSet(set);
// Нужна синхронизация для многопоточности?
List<String> syncList = Collections.synchronizedList(new ArrayList<>(list));
Map<String, User> syncMap = Collections.synchronizedMap(new HashMap<>(map));
// Нужна параллельная обработка?
List<Result> results = data.parallelStream()
.map(this::process)
.collect(Collectors.toList());
Заключение
Преобразование типов коллекций в Java:
- List ↔ Set — через конструктор или Stream API
- List ↔ Array — Arrays.asList() или toArray()
- List ↔ Map — Collectors.toMap() с функциями преобразования
- Set ↔ Map — через keySet(), values(), entrySet()
- Queue — LinkedList реализует оба интерфейса
- Фильтрация — filter() в Stream API
- Группировка — Collectors.groupingBy()
Выбирайте Stream API для новых проектов — это функциональный, читаемый и эффективный способ. Для производительности критичных операций анализируйте сложность выбранной коллекции.