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?
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:
Puede usar flatMap
para 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());
Class::method
Al 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.
El flatMap
método Stream
activado puede aplanar esas listas por usted, pero debe crear Stream
objetos para el elemento y luego Stream
para el resultado.
No necesitas todos esos Stream
objetos. 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 List
es Iterable
, este código llama al forEach
método (característica Java 8), que se hereda de Iterable
.
Realiza la acción dada para cada elemento del
Iterable
hasta 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 Iterator
devuelve 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.addAll
para 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).
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.
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);
}
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 ...
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);
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.
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]
::
:)