如何将列表列表转换为 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);

因为ListIterable,所以这段代码调用the forEach method(Java 8 特性),它继承自Iterable

Iterable 的每个元素执行给定的操作,直到所有元素都已处理完毕或该操作引发异常。如果指定了迭代顺序,则按迭代顺序执行操作。

ListIterator 按顺序返回项目。

对于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&lt;List&gt; 转换为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&lt;Documents&gt;,这个列表包含更多其他文档的列表,比如List&lt;Excel&gt;List&lt;Word&gt;List&lt;PowerPoint&gt;。所以结构是

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 IntStream 转换为列表?

如何使用流将列表转换为带有索引的地图 - Java 8?

如何使用 Java 8 Lambda 将对象列表转换为 Map<Object, Object>

Java 8如何操作一个列表中的对象并将其收集到另一个列表中? [重复]

如何将嵌套列表中的值转换为集合?

将对象列表转换为映射Java 8流