Есть ли способ преобразовать 2D-список в 1D-список, используя только «карту», ​​не используя «flatMap»?

Я новичок в Java, я только что узнал о map и flatMap.

Когда 2-й список должен быть преобразован в 1-й список, это было реализовано, как показано ниже.

List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));

List<Integer> lst1 = list_2d
    .stream()
    .flatMap(arr -> arr.stream())
    .collect(Collectors.toList());
printAll(lst1); // [1, 2, 3, 4]

Но мне кажется, что это можно реализовать и без flatMap.

Есть ли способ сделать код с той же логикой, просто используя map, а не flatMap?

Просто спрашиваю, потому что, если map может заменить все flatMap, нет причин запоминать flatMap. Я всегда стремлюсь к простым и базовым вещам.

Я не думаю, что вы можете преобразовать один элемент Stream в несколько элементов Stream без использования flatMap. map преобразует только один элемент в один элемент другого типа.

Eran 20.12.2020 11:16

@ОлеВ.В. Просто потому, что если карта может заменить всю плоскую карту, то незачем запоминать плоскую карту. Я всегда преследую простые и основные вещи.

frhyme 20.12.2020 11:28

@frhyme - I always pursue simple and basic thing. - flatMap действительно простая и основная вещь. Одна из причин, по которой Java стала настолько популярной, заключается в этих простых конструкциях/типах/операциях; в противном случае в C/C++ нам приходилось иметь дело со многими сложностями только потому, что не было таких простых и базовых вещей, например. подумайте о символах и строках в Java и C/C++.

Arvind Kumar Avinash 20.12.2020 11:49
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
3
584
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вы можете удалить flatMap, если выполните плоское отображение во время collect:

List<Integer> lst1 = list_2d
    .stream()
    .collect(Collectors.flatMapping (List::stream, Collectors.toList()));

Однако я не вижу преимущества использования Collectors.flatMapping вместо flatMap.

Вы также можете избавиться от Collectors.flatMapping, используя reduce напрямую.

Polygnome 20.12.2020 11:20
Ответ принят как подходящий

Чтобы ответить на поставленный вопрос, есть и другие способы, но я бы не рекомендовал ни один из тех, которые я могу придумать. Например, вы можете использовать сокращение:

    List<List<Integer>> list2d = List.of(List.of(1, 2), List.of(3, 4));

    List<Integer> lst1 = list2d
        .stream()
        .reduce((l1, l2) -> {
            ArrayList<Integer> concatenated = new ArrayList<>(l1);
            concatenated.addAll(l2);
            return concatenated;
        })
        .orElse(List.of()); // or else empty list
    
    System.out.println(lst1);

Вывод такой же как у вас:

[1, 2, 3, 4]

Но ваш код гораздо легче понять, чем мой. Я предлагаю вам придерживаться его.

Нельзя ли поменять местами map() и flatMap()?

Просто потому, что если карта может заменить всю плоскую карту, то нет причин запомнить flatMap. Я всегда преследую простые и основные вещи.

Вы уже получили самое простое и основное, что есть. Кроме того, чтобы использовать весь потенциал потоков, есть много вызовов методов, которые вам нужно будет использовать время от времени. После многолетнего использования потоков я все еще иногда ищу их в Javadoc. Мне пришлось искать детали reduce() для этого ответа. Не ждите, что у вас все будет в голове. Я знаю flatMap() наизусть, так как это часто практично.

Редактировать: Только из академического интереса: обычно вы не можете заменить flatMap() на map(). Или вы бы использовали map() с самого начала. Но наоборот: вы всегда можете заменить map() на flatMap(). Вы просто не хотели бы. Например, если бы у нас было:

    List<String> strings = List.of("short", "somewhat longer");
    List<Integer> lengths = strings.stream()
            .map(String::length)
            .collect(Collectors.toList());
    System.out.println(lengths);

[5, 15]

Если бы по какой-то странной причине мы могли помнить только flatMap(), а не map(), мы могли бы сделать:

    List<Integer> lengths = strings.stream()
            .flatMap(s -> Stream.of(s.length()))
            .collect(Collectors.toList());

Я думаю, ясно видеть, что все, что это приносит нам, — это ненужные осложнения. Лучше запомнить как map(), так и flatMap(), по крайней мере, достаточно хорошо, чтобы иметь возможность найти их, когда они нам понадобятся.

Возможно, это не то, что вы ищете, но если цель состоит в том, чтобы добавить все подсписки в один результирующий список, использование для каждого также может быть вариантом с точки зрения простого и базового:

List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
List<Integer> result = new ArrayList<>();
list_2d.forEach(list -> result.addAll(list));
System.out.println(result);

Поскольку уже есть несколько ответов с разными реализациями, я попытаюсь показать разницу между map и flatMap.

Операция map используется для замены каждого элемента потока ровно на один другой элемент. В вашем примере возможными вариантами использования могут быть либо выполнение некоторой агрегации в списке (например, суммирование всех элементов), либо получение List<Integer>, содержащего все суммы отдельных списков. Или вы можете изменить структуру данных, например, на набор, что даст List<Set<Integer>>. Количество элементов в результирующем списке всегда будет равно 2, поскольку в исходном списке было 2 элемента.

С другой стороны, операция flatMap используется для преобразования одного элемента в ноль или более элементов. Рассмотрим семейство классов, состоящее из нескольких людей. Если вы откуда-то получили список Семей, но вас интересуют только отдельные люди, вы можете flatMap Семейный список превратить в список Людей.

Разница между flatMap и реализациями, приведенными в других ответах, заключается в типе операции: flatMap — это промежуточная операция, то есть результатом является Stream. collect и reduce являются терминальными операциями, что означает, что результат будет другим (в вашем примере: List<Integer>. Если вы хотите выполнить больше потоковых операций (например, filter или другой map), вам придется создать еще один Stream из результирующего списка.

Чтобы проиллюстрировать это, рассмотрим список, указанный в вашем вопросе, и в результате вы хотите получить все четные числа из списка. Я использую collect решение из этого ответа. Как видите, использование flatMap избавляет от ненужного создания списка, а также от некоторых строк кода.

public static void usingFlatMap() {
    List<List<Integer>> originalList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));

    List<Integer> result = originalList
            .stream()
            .flatMap(list -> list.stream())
            .filter(number -> number % 2 == 0)
            .collect(Collectors.toList());

    System.out.println(result); // [2, 4]
}

public static void usingCollect() {
    List<List<Integer>> originalList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));

    List<Integer> intermediateList = originalList
            .stream()
            .collect(Collectors.flatMapping(List::stream, Collectors.toList()));
    List<Integer> result = intermediateList
            .stream()
            .filter(number -> number % 2 == 0)
            .collect(Collectors.toList());

    System.out.println(result); // [2, 4]
}

Вы можете перебирать этот 2d-список и добавлять элементы (целые числа) из него в другой 1d-список:


  1. List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
    
    List<Integer> list_1d = new ArrayList<>();
    
    IntStream.range(0, list_2d.size()).forEach(i ->
            IntStream.range(0, list_2d.get(i).size()).forEach(j ->
                    list_1d.add(list_2d.get(i).get(j))));
    
    System.out.println(list_1d); // [1, 2, 3, 4]
    

  1. List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
    
    List<Integer> list_1d = new ArrayList<>();
    
    list_2d.forEach(list ->
            list.forEach(element ->
                    list_1d.add(element)));
    
    System.out.println(list_1d); // [1, 2, 3, 4]
    

Другие вопросы по теме