如何将列表列表转换为 Java 8 中的列表?
Posted
技术标签:
【中文标题】如何将列表列表转换为 Java 8 中的列表?【英文标题】:How can I turn a List of Lists into a List in Java 8? 【发布时间】:2014-09-28 14:29:47 【问题描述】:如果我有一个List<List<Object>>
,如何使用 Java 8 的功能将其转换为包含相同迭代顺序的所有对象的List<Object>
?
【问题讨论】:
【参考方案1】:您可以使用flatMap
将内部列表(在将它们转换为Streams之后)扁平化为单个Stream,然后将结果收集到一个列表中:
List<List<Object>> list = ...
List<Object> flat =
list.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
【讨论】:
@arshajii true,但出于某种原因,我更喜欢 lambda 表达式。也许我不喜欢::
的样子:)
Class::method
起初感觉有点奇怪,但它的好处是它声明了你映射的对象类型。这是你在流中失去的东西。
但您可能希望拥有一个 List 容器,在这种情况下您可能需要使用 lambda (l->l.myList.stream())。
如果您需要进行显式转换(例如,将基元数组转换为 List),那么可能还需要 lambda。【参考方案2】:
flatmap
更好,但还有其他方法可以达到同样的效果
List<List<Object>> listOfList = ... // fill
List<Object> collect =
listOfList.stream()
.collect(ArrayList::new, List::addAll, List::addAll);
【讨论】:
请注意,使用 flatMap 您不会关闭流,因为使用 collect 需要再次重新打开它。【参考方案3】: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
,以按顺序添加内部列表元素。
将指定集合中的所有元素附加到此列表的末尾,按照指定集合的迭代器返回的顺序(可选操作)。
【讨论】:
很好的替代建议,可以避免一些不必要的分配。在处理一些较大的集合时,看看它的顶部堆使用情况会很有趣,看看它们是如何相互比较的。【参考方案4】:正如@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);
【讨论】:
【参考方案5】:将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]
【讨论】:
这应该被标记为正确答案【参考方案6】:您可以使用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 的贡献者。
【讨论】:
当Java 8提供功能时,为什么要使用第三方依赖? Eclipse Collections API 在集合本身上,所以代码简洁,是本例的主要原因之一。【参考方案7】:我只是想再解释一个场景,比如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.
我希望这可以解决某人在编码时遇到的问题...
【讨论】:
【参考方案8】: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 语句。
【讨论】:
为什么不使用递归? :) 根据我多年的经验,Java 等语言中的递归是危险的,因为它很容易意外吃掉系统的所有资源。你可以做到,但我不会。但也许另一种选择是使用循环来构建嵌套流,然后运行该流。根据我的理解,您不能真正在流中实现无限循环。【参考方案9】:我们可以使用平面地图,请参考以下代码:
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);
【讨论】:
【参考方案10】:由于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();
【讨论】:
【参考方案11】:List list = map.values().stream().collect(Collectors.toList());
List<Employee> employees2 = new ArrayList<>();
list.stream().forEach(
n-> employees2.addAll(n));
【讨论】:
假设我在上面的代码中有 List> 请阅读this帮助页面,了解如何正确格式化代码。此外,您可以随时edit您的答案,而不是发布后续 cmets。 虽然此代码可能会解决问题,including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提出问题的人。请edit您的答案添加解释并说明适用的限制和假设。 感谢 Yunnosch 的建议..我会做的..快乐编码!以上是关于如何将列表列表转换为 Java 8 中的列表?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 8 Lambda 将对象列表转换为 Map<Object, Object>