Как автоматически увеличить ключ hashmap с помощью сборщиков и потока в Java 8 - PullRequest
0 голосов
/ 26 июня 2018

Я новичок в Java 8: Streams и Collectors классы.

Я читаю файл, содержимое которого необходимо сохранить в LinkedHashMap<Integer, String>, где его <keys> - номера строк файлов, а <values> - содержимое в каждой строке, соответственно.

Здесь я хочу использовать концепцию Stream, но я не могу использовать Collectors.toMap для автоматического увеличения <keys>, которое необходимо сохранить в объекте LinnkedHashMap. Вместо этого я получаю исключения.

Ниже приведен код, который я пытаюсь:

List<String> list = new ArrayList<>();
Integer count = 0;

try (BufferedReader br = Files.newBufferedReader( Paths.get( fileName ) )) {
    // br returns as stream and convert it into a List
    list = br.lines().collect( Collectors.toList() );
}
catch ( IOException e ) {
    e.printStackTrace();
}

list.forEach( System.out::println );

Map<Integer, String> fileNumWithContentMapper = list.stream()
        .collect( Collectors.toMap( n->n+1,s1->s1));

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

вы можете использовать IntStream.range:

IntStream.range(0, list.size())
         .boxed()
         .collect(Collectors.toMap(Function.identity(), i -> list.get(i)));

Другим вариантом будет использование LineNumberReader API.

0 голосов
/ 22 мая 2019

Есть много способов это сделать. Но здесь я объясню свой путь:

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>
0 голосов
/ 26 июня 2018

попробуйте этот код:

public static void main(String[] args)  {
    List<String> list = Arrays.asList("A", "B", "C");
    list.forEach( System.out::println );

    AtomicInteger i = new AtomicInteger(0);
    Map<Integer, String> fileNumWithContentMapper = list.stream()
                .collect( Collectors.toMap( n->i.incrementAndGet(),s1->s1));

    System.out.println(fileNumWithContentMapper);
}
...