【发布时间】:2014-09-28 14:29:47
【问题描述】:
如果我有一个List<List<Object>>,如何使用 Java 8 的功能将其转换为包含相同迭代顺序的所有对象的List<Object>?
【问题讨论】:
标签: java collections java-8
如果我有一个List<List<Object>>,如何使用 Java 8 的功能将其转换为包含相同迭代顺序的所有对象的List<Object>?
【问题讨论】:
标签: java collections java-8
您可以使用flatMap 将内部列表(在将它们转换为Streams之后)扁平化为单个Stream,然后将结果收集到一个列表中:
List<List<Object>> list = ...
List<Object> flat =
list.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
【讨论】:
:: 的样子:)
Class::method 起初感觉有点奇怪,但它的好处是它声明了你映射的对象类型。这是你在流中失去的东西。
flatmap 更好,但还有其他方法可以达到同样的效果
List<List<Object>> listOfList = ... // fill
List<Object> collect =
listOfList.stream()
.collect(ArrayList::new, List::addAll, List::addAll);
【讨论】:
Stream 上的 flatMap 方法当然可以为您展平这些列表,但它必须为元素创建 Stream 对象,然后为结果创建 Stream。
您不需要所有这些 Stream 对象。这是执行任务的简单、简洁的代码。
// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);
因为List 是Iterable,所以这段代码调用the forEach method(Java 8 特性),它继承自Iterable。
对
Iterable的每个元素执行给定的操作,直到所有元素都已处理完毕或该操作引发异常。如果指定了迭代顺序,则按迭代顺序执行操作。
List 的 Iterator 按顺序返回项目。
对于Consumer,此代码将方法引用(Java 8 特性)传递给Java 8 之前的方法List.addAll,以按顺序添加内部列表元素。
将指定集合中的所有元素附加到此列表的末尾,按照指定集合的迭代器返回的顺序(可选操作)。
【讨论】:
正如@Saravana 提到的:
平面图更好,但还有其他方法可以达到同样的效果
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);
}
【讨论】:
将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]
在 Python 中,这可以使用列表理解来完成。
list_of_lists = [['Roopa','Roopi','Tabu', 'Soudipta'],[180.0, 1231, 2112, 3112], [130], [158.2], [220.2]]
flatten = [val for sublist in list_of_lists for val in sublist]
print(flatten)
['Roopa', 'Roopi', 'Tabu', 'Soudipta', 180.0, 1231, 2112, 3112, 130, 158.2, 220.2]
【讨论】:
您可以使用Eclipse Collections 中的flatCollect() 模式。
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 Collections 的贡献者。
【讨论】:
我只是想再解释一个场景,比如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.
}
我希望这可以解决某人在编码时遇到的问题...
【讨论】:
Eran 的答案是最佳答案的扩展,如果您有一堆列表层,您可以继续对它们进行平面映射。
这还提供了一种方便的过滤方式,如果需要,您还可以向下层进行过滤。
例如:
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 语句。
【讨论】:
我们可以使用平面地图,请参考以下代码:
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);
【讨论】:
由于java-16,您可以使用Stream#mapMulti
List<Object> result = listOfLists.stream()
.mapMulti((List<Object> list, Consumer<Object> consumer) -> {
list.forEach(consumer::accept);
})
.collect(Collectors.toList());
如果您需要不可变的List,您甚至可以使用toList() 作为终端操作
List<Object> result = listOfLists.stream()
.mapMulti((List<Object> list, Consumer<Object> consumer) -> {
list.forEach(consumer::accept);
})
.toList();
【讨论】:
List list = map.values().stream().collect(Collectors.toList());
List<Employee> employees2 = new ArrayList<>();
list.stream().forEach(
n-> employees2.addAll(n));
【讨论】: