Есть много способов это сделать. Но здесь я объясню свой путь:
1. Установите размер объекта IntStream
.
Во-первых, когда у вас есть List<E>
объектов (т.е. E
может быть String
, Integers
, Objects
и т. Д.), Вы можете преобразовать его в Map<Integer, E>
с помощью IntStream
учебный класс. Этот класс представляет собой последовательность примитивных int-значимых элементов, которые поддерживают последовательные и параллельные агрегатные операции. Это значит как огромный счетчик. Если у нас уже есть счетчик, нам нужно установить некоторые ограничения, и метод IntStream.range(int start, int end)
поможет нам. Этот метод возвращает последовательно упорядоченный IntStream
от start
(включительно) до end
(эксклюзив) с шагом 1. Поэтому, если вы хотите создать IntStream
с размером нашего List
использования это:
List<Integer> numbers = Arrays.asList(4, 5, 4, 3);
IntStream stream = IntStream.range(0, numbers.size);
2. Подготовьте объект Stream
на основе объекта IntStream
.
Теперь у нас есть счетчик размера вашего List<E>
, но нам нужен Map<Integer, E>
. Ну, теперь мы будем использовать IntStream.boxed()
. Этот метод возвращает Stream
, состоящий из элементов этого потока, каждый из которых упакован в Integer
. Это Stream<Integer>
. Мы почти закончили.
Stream<Integer> streamBoxed = stream.boxed();
3. Преобразовать объект Stream
в Map
объект
Наконец, мы можем создать карту, используя метод Stream.collect()
. Этот метод выполняет изменяемую операцию сокращения над элементами этого потока. Это сокращение будет сложным, если у нас не было помощи Collectors.toMap()
метода. Этот сборщик можно использовать для сбора элементов Stream в экземпляр Map. Для этого нам нужно предоставить две функции: keyMapper
и valueMapper
. keyMapper
будет использоваться для извлечения ключа Map
из элемента Stream
, а valueMapper
будет использоваться для извлечения <value>
, связанного с данным <key>
. Для нашего примера мы будем использовать Map<Integer, Integer>
. keyMapper
будет значениями потока steamBoxed
, которые мы можем извлечь, используя i -> i
, а valueMapper
должны быть значениями списка numbers
, которые мы получим, используя i -> numbers.get(i)
, например так:
Map<Integer, Integer> result = streamBoxed.collect(Collectors.toMap(i -> i, i -> numbers.get(i)))
4. Объедините все шаги
Эти три части можно объединить в один простой код:
List<Integer> numbers = Arrays.asList(4, 5, 4, 3);
Map<Integer, Integer> result = IntStream
.range(0, numbers.size); // IntStream
.boxed(); // Stream<Integer>
.collect(Collectors.toMap(i -> i, i -> numbers.get(i))) // Map<Integer, Integer>
Также вы обнаружите, что некоторые авторы предпочитают использовать метод Function.identity()
в качестве keyMapper
и numbers::get
лямбда-выражения в качестве valueMapper
. Почему они используют эти выражения? Просто для предпочтения. Метод Function.identity()
всегда будет возвращать один и тот же экземпляр. Таким образом, использование Function.identity()
вместо i -> i
может сэкономить память. Тем не менее, i -> i
является более читабельным, чем Function.identity()
, но, поскольку создает свой собственный экземпляр и имеет отдельный класс реализации, потребляет больше памяти. ::
лямбда-выражение - это просто захват ссылки на метод.
Но как я могу применить это к моему решению?
Ну вот так:
final List<String> list;
...
// list initialization;
list = br.lines().collect(Collectors.toList());
...
Map<Integer, String> fileNumWithContentMapper = IntStream
.range(0, list.size()) // IntStream
.boxed() // Stream<Integer>
.collect(Collectors.toMap(i -> i, i -> list.get(i))); // Map<Integer, String>
альтернатива
final List<String> list;
...
// list initialization;
list = br.lines().collect(Collectors.toList());
...
Map<Integer, String> fileNumWithContentMapper = IntStream
.range(0, list.size()) // IntStream
.boxed() // Stream<Integer>
.collect(Collectors.toMap(Function.identity(), list::get)) // Map<Integer, String>