Как я могу превратить список списков в список в Java 8?

534

Если у меня есть List<List<Object>>, как я могу превратить это в, List<Object>который содержит все объекты в том же порядке итерации, используя функции Java 8?

Сара Сзабо
источник

Ответы:

952

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

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());
Эран
источник
11
@arshajii верно, но по какой-то причине я предпочитаю лямбда-выражение. Возможно, мне не нравится внешний вид :::)
Eran
25
Class::methodсначала кажется немного странным, но имеет то преимущество, что объявляет, из какого типа объекта вы отображаете. Это то, что вы иначе потеряете в потоках.
ArneHugo
2
Тем не менее, вы можете захотеть иметь контейнер List, и в этом случае вам может понадобиться использовать лямбду (l-> l.myList.stream ()).
Миох
2
Если вам нужно выполнить явное приведение (например, массив примитивов к List), то лямбды также могут быть необходимы.
Майкл Фултон
52

flatmap лучше, но есть и другие способы достичь того же

List<List<Object>> listOfList = ... // fill

List<Object> collect = 
      listOfList.stream()
                .collect(ArrayList::new, List::addAll, List::addAll);
Saravana
источник
37

flatMapМетод на , Streamбезусловно , может придавить эти списки для вас, но он должен создавать Streamобъекты для элемента, то Streamдля результата.

Вам не нужны все эти Streamобъекты. Вот простой и лаконичный код для выполнения задачи.

// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);

Так как Listэто Iterable, этот код вызывает в forEachметод (Java 8 функций), который наследуется от Iterable.

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

И А List«S Iteratorвозвращает элементы в последовательном порядке.

Для этого Consumerэтот код передает ссылку на метод (функция Java 8) в метод перед Java 8 List.addAllдля последовательного добавления элементов внутреннего списка.

Добавляет все элементы в указанной коллекции в конец этого списка в том порядке, в котором они возвращаются итератором указанной коллекции (необязательная операция).

rgettman
источник
3
Хорошее альтернативное предложение, которое позволяет избежать ненужных выделений. Было бы интересно увидеть, как это используется при работе с некоторыми большими коллекциями, чтобы сравнить их друг с другом.
За Лундберг
12

Вы можете использовать flatCollect()шаблон из Eclipse Collections .

MutableList<List<Object>> list = Lists.mutable.empty();
MutableList<Object> flat = list.flatCollect(each -> each);

Если вы не можете изменить список из List:

List<List<Object>> list = new ArrayList<>();
List<Object> flat = ListAdapter.adapt(list).flatCollect(each -> each);

Примечание: я участвую в коллекциях Eclipse.

Нихил Нанивадекар
источник
21
зачем использовать сторонние зависимости, если функциональность обеспечивается Java 8?
saw303
2
Eclipse Collections API находится в самой коллекции, поэтому код является лаконичным и является одной из основных причин в этом случае.
Нихил Нанивадекар
11

Так же, как @Saravana упомянул:

flatmap лучше, но есть и другие способы добиться того же

 listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
 });

Подводя итог, существует несколько способов добиться того же:

private <T> List<T> mergeOne(Stream<List<T>> listStream) {
    return listStream.flatMap(List::stream).collect(toList());
}

private <T> List<T> mergeTwo(Stream<List<T>> listStream) {
    List<T> result = new ArrayList<>();
    listStream.forEach(result::addAll);
    return result;
}

private <T> List<T> mergeThree(Stream<List<T>> listStream) {
    return listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
    });
}

private <T> List<T> mergeFour(Stream<List<T>> listStream) {
    return listStream.reduce((l1, l2) -> {
        List<T> l = new ArrayList<>(l1);
        l.addAll(l2);
        return l;
    }).orElse(new ArrayList<>());
}

private <T> List<T> mergeFive(Stream<List<T>> listStream) {
    return listStream.collect(ArrayList::new, List::addAll, List::addAll);
}
Hearen
источник
11

Я просто хочу объяснить еще один сценарий , как List<Documents>, этот список содержит несколько списков других документов , таких как List<Excel>, List<Word>, List<PowerPoint>. Итак, структура

class A {
  List<Documents> documentList;
}

class Documents {
  List<Excel> excels;
  List<Word> words;
  List<PowerPoint> ppt;
}

Теперь, если вы хотите перебрать Excel только из документов, сделайте что-то вроде ниже.

Так что код будет

 List<Documents> documentList = new A().getDocumentList();

 //check documentList as not null

 Optional<Excel> excelOptional = documentList.stream()
                         .map(doc -> doc.getExcel())
                         .flatMap(List::stream).findFirst();
 if(excelOptional.isPresent()){
   Excel exl = optionalExcel.get();
   // now get the value what you want.
 }

Я надеюсь, что это может решить чью-то проблему во время кодирования ...

Kushwaha
источник
1

Мы можем использовать flatmap для этого, пожалуйста, обратитесь ниже код:

 List<Integer> i1= Arrays.asList(1, 2, 3, 4);
 List<Integer> i2= Arrays.asList(5, 6, 7, 8);

 List<List<Integer>> ii= Arrays.asList(i1, i2);
 System.out.println("List<List<Integer>>"+ii);
 List<Integer> flat=ii.stream().flatMap(l-> l.stream()).collect(Collectors.toList());
 System.out.println("Flattened to List<Integer>"+flat);
Пратик Павар
источник
1

Расширение ответа Эрана, которое было главным ответом, если у вас есть несколько слоев списков, вы можете продолжать отображать их.

Это также идет с удобным способом фильтрации, когда вы спускаетесь по слоям, если это необходимо.

Так, например:

List<List<List<List<List<List<Object>>>>>> multiLayeredList = ...

List<Object> objectList = multiLayeredList
    .stream()
    .flatmap(someList1 -> someList1
        .stream()
        .filter(...Optional...))
    .flatmap(someList2 -> someList2
        .stream()
        .filter(...Optional...))
    .flatmap(someList3 -> someList3
        .stream()
        .filter(...Optional...))
    ...
    .collect(Collectors.toList())

В SQL это похоже на наличие операторов SELECT внутри операторов SELECT.

cody.tv.weber
источник
1

Метод для преобразования List<List>в List:

listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

Смотрите этот пример:

public class Example {

    public static void main(String[] args) {
        List<List<String>> listOfLists = Collections.singletonList(Arrays.asList("a", "b", "v"));
        List<String> list = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

        System.out.println("listOfLists => " + listOfLists);
        System.out.println("list => " + list);
    }

}       

Это печатает:

listOfLists => [[a, b, c]]
list => [a, b, c]
Судипта Датта
источник