Есть ли предпочтительный способ собирать поток списков в плоский список?

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

final Stream<List<Integer>> stream = Stream.empty();
final List<Integer> one = stream.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
final List<Integer> two = stream.flatMap(List::stream).collect(Collectors.toList());

Второй вариант выглядит намного приятнее для меня, но я думаю, что первый из них более эффективен в параллельных потоках. Существуют ли дополнительные аргументы для одного из двух методов или против этого?

Ответ 1

Основное отличие состоит в том, что flatMap является промежуточной операцией , а collect является операцией терминала .

Итак, flatMap - единственный способ обработки сплющенных элементов потока, если вы хотите выполнить другие операции, кроме collect ing.

Далее collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll) очень сложно прочитать, учитывая тот факт, что у вас есть две идентичные ссылки на методы ArrayList::addAll с совершенно другой семантикой.

Что касается параллельной обработки, ваша догадка неверна. Первый имеет меньшие возможности параллельной обработки, поскольку он полагается на ArrayList.addAll, применяемый к элементам потока (под-спискам), которые не могут быть разбиты на параллельные подэтапы. Напротив, Collectors.toList(), примененный к flatMap, может выполнять параллельную обработку элементов суб-списка, если конкретный List, встречающийся в потоке, поддерживает его. Но это будет актуально только в том случае, если у вас довольно небольшой поток довольно больших подписок.

Единственным недостатком flatMap является создание промежуточного потока, которое добавляет накладные расходы в случае, когда у вас много очень маленьких подписок.

Но в вашем примере поток пуст, поэтому он не имеет значения (scnr).

Ответ 2

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

По сути, цель того, что вы делаете, - это плоская карта. Если в этом случае я ожидал увидеть плоскую карту вместо использования addAll().