如何在Java 8中将列表列表转换为列表?

Sar*_*abo 470 java collections java-8

如果我有一个List<List<Object>>,我如何List<Object>通过使用Java 8的功能将其转换为包含相同迭代顺序中的所有对象?

Era*_*ran 857

您可以使用flatMap内部列表(将它们转换为Streams后)展平为单个Stream,然后将结果收集到列表中:

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

  • "Class :: method"起初感觉有些奇怪,但它的好处是它声明了你映射的是什么类型的对象.这是你在流中丢失的东西. (21认同)
  • @arshajii是的,但由于某种原因,我更喜欢lambda表达式.也许我不喜欢`::`:)的样子 (10认同)
  • 从 Java 16 开始,可以使用“.toList()”代替“.collect(Collectors.toList())”。 (10认同)
  • 但是你可能想要一个List的容器,在这种情况下你可能需要使用lambda(l-> l.myList.stream()). (2认同)
  • 如果您需要进行显式转换(例如 List 的基元数组),那么 lambdas 也可能是必要的。 (2认同)

Sou*_*tta 64

List<List>将 a 转换为 的方法List

listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

看这个例子:

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);
    }

}       
Run Code Online (Sandbox Code Playgroud)

它打印:

listOfLists => [[a, b, c]]
list => [a, b, c]
Run Code Online (Sandbox Code Playgroud)

在 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)
Run Code Online (Sandbox Code Playgroud)
['Roopa', 'Roopi', 'Tabu', 'Soudipta', 180.0, 1231, 2112, 3112, 130, 158.2, 220.2]
Run Code Online (Sandbox Code Playgroud)

  • 这应该被标记为正确答案 (7认同)

Sar*_*ana 43

flatmap 更好,但还有其他方法来实现同样的目标

List<List<Object>> listOfList = ... // fill

List<Object> collect = 
      listOfList.stream()
                .collect(ArrayList::new, List::addAll, List::addAll);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,使用 flatMap 您不会关闭流,因为使用收集时您将需要再次重新打开它。 (2认同)

rge*_*man 23

flatMap方法上Stream肯定可以拼合那些列出了你,但它必须创建Stream的元素对象,那么Stream该结果.

您不需要所有这些Stream对象.这是执行任务的简单,简洁的代码.

// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);
Run Code Online (Sandbox Code Playgroud)

由于ListIterable,这个代码调用forEach方法(爪哇8功能),它是从继承Iterable.

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

ListIterator返回顺序的项目.

对于Consumer此代码,此代码将方法引用(Java 8功能)传递给Java 8之前的方法,List.addAll以按顺序添加内部列表元素.

将指定集合中的所有元素按指定集合的​​迭代器(可选操作)返回的顺序追加到此列表的末尾.

  • 一个很好的替代建议,可以避免一些不必要的分配。在处理一些较大的集合时,看看它的最大堆用法,以及如何将它们相互比较会很有趣。 (2认同)

Nik*_*kar 12

您可以使用Eclipse Collections中flatCollect()模式.

MutableList<List<Object>> list = Lists.mutable.empty();
MutableList<Object> flat = list.flatCollect(each -> each);
Run Code Online (Sandbox Code Playgroud)

如果您无法更改列表List:

List<List<Object>> list = new ArrayList<>();
List<Object> flat = ListAdapter.adapt(list).flatCollect(each -> each);
Run Code Online (Sandbox Code Playgroud)

注意:我是Eclipse Collections的贡献者.

  • 为什么在Java 8提供功能时使用第三方依赖? (18认同)
  • Eclipse Collections API是集合本身,所以代码简洁,是这种情况下的主要原因之一. (2认同)

Hea*_*ren 9

就像@Saravana提到的那样:

平面图更好,但还有其他方法可以实现相同的效果

 listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
 });
Run Code Online (Sandbox Code Playgroud)

总结起来,有几种方法可以达到以下目的:

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);
}
Run Code Online (Sandbox Code Playgroud)


Kus*_*aha 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;
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您只想从文档中迭代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.
 }
Run Code Online (Sandbox Code Playgroud)

我希望这可以解决编码时某人的问题...


cod*_*ber 7

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())
Run Code Online (Sandbox Code Playgroud)

这在 SQL 中类似于在 SELECT 语句中包含 SELECT 语句。