Подтвердить что ты не робот

Как использовать потоки для поиска пар элементов из двух списков или умножения массива

У меня есть два списка чисел, и я бы хотел найти все возможные пары чисел. Например, учитывая списки [1, 2, 3] и [3, 4], результат должен быть [(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)].

Я знаю, что могу сделать это, используя цикл for, но есть ли более сжатый способ сделать это с использованием потоков Java 8?

Я пробовал следующее, но у меня что-то не хватает, поскольку я получаю List<Stream<int[]>> вместо List<int[]>.

public static void main(String[] args) {
    List<Integer> list1 = Arrays.asList(1, 2, 3);
    List<Integer> list2 = Arrays.asList(3, 4);
    List<int[]> pairs = list1.stream().map(i -> list2.stream().map(j -> new int[] { i, j }))
            .collect(Collectors.toList());
    pairs.forEach(i -> {
            System.out.println("{" + i[0]+ "," + i[1]+ "}");
    });
}
4b9b3361

Ответ 1

Вам просто нужно заменить свой первый map() на flatMap().

Ответ 3

Вот решение, использующее IntStream с двумя массивами int в качестве источника вместо List<Integer>. Я хотел посмотреть, удалось ли решить эту проблему без бокса в каждом int как Integer.

int[] one = new int[]{1, 2, 3};
int[] two = new int[]{3, 4};
List<IntIntPair> list = new ArrayList<>();
IntStream.of(one).forEach(i ->
        IntStream.of(two).mapToObj(j -> PrimitiveTuples.pair(i, j)).forEach(list::add));
System.out.println(list);
// [1:3, 1:4, 2:3, 2:4, 3:3, 3:4]

К сожалению, я не мог использовать flatMap в IntStream, поскольку он возвращает IntStream. В настоящее время на IntStream нет flatMapToObj, что и нужно здесь. Поэтому я использовал forEach.

IntIntPair и PrimitiveTuples классы, которые я использовал из Eclipse Collections, поскольку они упростили просто вывод списка в виде строки. Вы можете использовать int[], как в своем решении. Код выглядит следующим образом.

List<int[]> list = new ArrayList<>();
IntStream.of(one).forEach(i ->
        IntStream.of(two).mapToObj(j -> new int[]{i, j}).forEach(list::add));

В выпуске Eclipse Collections, выпущенном в 8.1 (выпущен в середине марта), теперь есть метод flatCollect для всех примитивных контейнеров в библиотеке, которые могут быть использованы для решения этой проблемы. Это существенно делает метод flatMapToObj на IntStream.

IntList a = IntLists.mutable.with(1, 2, 3);
IntList b = IntLists.mutable.with(3, 4);
List<IntIntPair> result =
        a.flatCollect(
                i -> b.collect(j -> PrimitiveTuples.pair(i, j)),
                Lists.mutable.empty());
System.out.println(result);
// [1:3, 1:4, 2:3, 2:4, 3:3, 3:4]

Обновление:

Как отмечалось в комментариях Бориса Спайдера, решение forEach не было бы потокобезопасным и сломалось бы, если IntStream был parallel. Следующее решение должно работать последовательно или параллельно. Я рад, что это было указано, потому что я не думал делать mapToObj на IntStream, а затем следует flatMap.

int[] one = new int[]{1, 2, 3};
int[] two = new int[]{3, 4};
List<int[]> list = IntStream.of(one).parallel()
        .mapToObj(i -> IntStream.of(two).mapToObj(j -> new int[]{i, j}))
        .flatMap(e -> e)
        .collect(Collectors.toList());
list.stream().map(e -> "{" + e[0] + "," + e[1] + "}").forEach(System.out::println);

Примечание. Я - коммиттер для Коллекции Eclipse.

Ответ 4

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

list1.stream()
     .flatMap(i -> list2.stream().map(j -> "{" + i+ "," + j + "}"))
     .forEach(System.out::println);