Se eu tiver um List<List<Object>>
, como posso transformá-lo em um List<Object>
que contenha todos os objetos na mesma ordem de iteração usando os recursos do Java 8?
Se eu tiver um List<List<Object>>
, como posso transformá-lo em um List<Object>
que contenha todos os objetos na mesma ordem de iteração usando os recursos do Java 8?
Respostas:
Você pode usar flatMap
para achatar as listas internas (após convertê-las em Streams) em um único Stream e, em seguida, coletar o resultado em uma lista:
List<List<Object>> list = ...
List<Object> flat =
list.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
Class::method
parece um pouco estranho no começo, mas tem o benefício de declarar de que tipo de objeto você está mapeando. Isso é algo que você perde nos fluxos.
O flatMap
método on Stream
certamente pode achatar essas listas para você, mas deve criar Stream
objetos para o elemento e, em seguida, umStream
para o resultado.
Você não precisa de todos esses Stream
objetos. Aqui está o código simples e conciso para executar a tarefa.
// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);
Como a List
é Iterable
, esse código chama o forEach
método (recurso Java 8), que é herdado de Iterable
.
Executa a ação especificada para cada elemento do
Iterable
até que todos os elementos tenham sido processados ou a ação lança uma exceção. As ações são executadas na ordem da iteração, se essa ordem for especificada.
E um List
's Iterator
retorna itens em ordem seqüencial.
Para Consumer
isso, esse código passa uma referência de método (recurso Java 8) para o método pré-Java 8 List.addAll
para adicionar os elementos da lista interna sequencialmente.
Anexa todos os elementos da coleção especificada ao final desta lista, na ordem em que são retornados pelo iterador da coleção especificada (operação opcional).
Você pode usar o flatCollect()
padrão nas Coleções Eclipse .
MutableList<List<Object>> list = Lists.mutable.empty();
MutableList<Object> flat = list.flatCollect(each -> each);
Se você não pode alterar a lista de List
:
List<List<Object>> list = new ArrayList<>();
List<Object> flat = ListAdapter.adapt(list).flatCollect(each -> each);
Nota: Sou colaborador do Eclipse Collections.
Assim como @Saravana mencionou:
O flatmap é melhor, mas existem outras maneiras de obter o mesmo
listStream.reduce(new ArrayList<>(), (l1, l2) -> {
l1.addAll(l2);
return l1;
});
Em resumo, existem várias maneiras de obter o mesmo da seguinte maneira:
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);
}
Eu só quero explicar mais um cenário como List<Documents>
, esta lista contém mais algumas listas de outros documentos, como List<Excel>
, List<Word>
, List<PowerPoint>
. Então a estrutura é
class A {
List<Documents> documentList;
}
class Documents {
List<Excel> excels;
List<Word> words;
List<PowerPoint> ppt;
}
Agora, se você deseja iterar o Excel apenas a partir de documentos, faça algo como abaixo.
Então o código seria
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 isso possa resolver o problema de alguém durante a codificação ...
Podemos usar o flatmap para isso, consulte o código abaixo:
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);
Uma expansão na resposta de Eran, que foi a principal, se você tiver várias camadas de listas, poderá continuar mapeando-as.
Isso também vem com uma maneira prática de filtrar conforme você desce as camadas, se necessário também.
Então, por exemplo:
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())
Isso seria semelhante no SQL a ter instruções SELECT nas instruções SELECT.
Método para converter um List<List>
para List
:
listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
Veja este exemplo:
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]
::
:)