¿Cómo puedo convertir una lista de listas en una lista en Java 8?


534

Si tengo una List<List<Object>>, ¿cómo puedo convertirla en una List<Object>que contenga todos los objetos en el mismo orden de iteración utilizando las características de Java 8?

Respuestas:


952

Puede usar flatMappara aplanar las listas internas (después de convertirlas en Streams) en una sola Stream y luego recopilar el resultado en una lista:

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());

11
@arshajii cierto, pero por alguna razón prefiero la expresión lambda. Quizás no me gusta el aspecto de :::)
Eran

25
Class::methodAl principio se siente un poco extraño, pero tiene la ventaja de que declara desde qué tipo de objeto está mapeando. Eso es algo que de lo contrario se pierde en las transmisiones.
ArneHugo

2
Sin embargo, es posible que desee tener un contenedor de una Lista y, en ese caso, es posible que deba usar lambda (l-> l.myList.stream ()).
Myoch

2
Si necesita hacer una conversión explícita (matriz de primitivas a la Lista, por ejemplo), entonces lambdas también puede ser necesario.
Michael Fulton

52

flatmap es mejor pero hay otras formas de lograr lo mismo

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

List<Object> collect = 
      listOfList.stream()
                .collect(ArrayList::new, List::addAll, List::addAll);

37

El flatMapmétodo Streamactivado puede aplanar esas listas por usted, pero debe crear Streamobjetos para el elemento y luego Streampara el resultado.

No necesitas todos esos Streamobjetos. Aquí está el código simple y conciso para realizar la tarea.

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

Como Listes Iterable, este código llama al forEachmétodo (característica Java 8), que se hereda de Iterable.

Realiza la acción dada para cada elemento del Iterablehasta que todos los elementos hayan sido procesados ​​o la acción arroje una excepción. Las acciones se realizan en el orden de iteración, si se especifica ese orden.

Y una List's Iteratordevuelve los artículos en orden secuencial.

Para el Consumer, este código pasa en una referencia de método (característica de Java 8) al método anterior a Java 8 List.addAllpara agregar los elementos de la lista interna secuencialmente.

Agrega todos los elementos de la colección especificada al final de esta lista, en el orden en que los devuelve el iterador de la colección especificada (operación opcional).


3
Buena sugerencia alternativa que evita algunas asignaciones innecesarias. Sería interesante ver el uso principal de este montón cuando se trabaja con algunas colecciones más grandes, para ver cómo se comparan entre sí.
Por Lundberg

12

Puede usar el flatCollect()patrón de las colecciones de Eclipse .

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

Si no puede cambiar la lista de List:

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

Nota: Soy colaborador de Eclipse Collections.


21
¿Por qué utilizar una dependencia de terceros cuando Java 8 proporciona la funcionalidad?
saw303

2
Eclipse Collections API está en la colección misma, por lo que el código es conciso, es una de las razones principales en este caso.
Nikhil Nanivadekar

11

Tal como mencionó @Saravana:

flatmap es mejor pero hay otras formas de lograr lo mismo

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

En resumen, hay varias formas de lograr lo mismo de la siguiente manera:

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);
}

11

Sólo quiero explicar un escenario más bien List<Documents>, esta lista contiene unas cuantas listas de otros documentos como List<Excel>, List<Word>, List<PowerPoint>. Entonces la estructura es

class A {
  List<Documents> documentList;
}

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

Ahora, si desea iterar Excel solo a partir de documentos, haga lo siguiente:

Entonces el código sería

 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.
 }

Espero que esto pueda resolver el problema de alguien mientras se codifica ...


1

Podemos usar flatmap para esto, consulte el siguiente código:

 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

Una expansión en la respuesta de Eran que fue la respuesta principal, si tienes un montón de capas de listas, puedes seguir mapeándolas.

Esto también viene con una forma práctica de filtrado a medida que baja las capas si es necesario también.

Así por ejemplo:

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())

Esto sería similar en SQL a tener instrucciones SELECT dentro de las instrucciones SELECT.


1

Método para convertir un List<List>a List:

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

Ver este ejemplo:

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);
    }

}       

Imprime:

listOfLists => [[a, b, c]]
list => [a, b, c]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.