展平集合

Ton*_*nis 58 java collections

说我有一个 Map<? extends Object, List<String>>

我可以很容易地获得地图的值,并迭代它以生成单个List<String>.

   for (List<String> list : someMap.values()) {
        someList.addAll(list);
    }
Run Code Online (Sandbox Code Playgroud)

有没有办法一次性压扁它?

  List<String> someList = SomeMap.values().flatten();
Run Code Online (Sandbox Code Playgroud)

wum*_*mpz 68

使用Java 8,如果您不想自己实例化List实例,就像在建议(和接受)的解决方案中一样

someMap.values().forEach(someList::addAll);
Run Code Online (Sandbox Code Playgroud)

您可以使用此语句进行流式处理:

List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

顺便说一句,有趣的是,在Java 8上,接受的版本似乎确实是最快的.它与a的时间大致相同

for (List<String> item : someMap.values()) ...
Run Code Online (Sandbox Code Playgroud)

并且比纯流媒体解决方案更快.这是我的小测试代码.我明确地没有将其命名为基准,以避免由此引发的基准缺陷讨论.;)我做了两次测试,希望得到一个完整的编译版本.

    Map<String, List<String>> map = new HashMap<>();
    long millis;

    map.put("test", Arrays.asList("1", "2", "3", "4"));
    map.put("test2", Arrays.asList("10", "20", "30", "40"));
    map.put("test3", Arrays.asList("100", "200", "300", "400"));

    int maxcounter = 1000000;

    System.out.println("1 stream flatmap");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
    }
    System.out.println(System.currentTimeMillis() - millis);

    System.out.println("1 parallel stream flatmap");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> someList = map.values().parallelStream().flatMap(c -> c.stream()).collect(Collectors.toList());
    }
    System.out.println(System.currentTimeMillis() - millis);

    System.out.println("1 foreach");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> mylist = new ArrayList<String>();
        map.values().forEach(mylist::addAll);
    }
    System.out.println(System.currentTimeMillis() - millis);        

    System.out.println("1 for");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> mylist = new ArrayList<String>();
        for (List<String> item : map.values()) {
            mylist.addAll(item);
        }
    }
    System.out.println(System.currentTimeMillis() - millis);


    System.out.println("2 stream flatmap");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());
    }
    System.out.println(System.currentTimeMillis() - millis);

    System.out.println("2 parallel stream flatmap");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> someList = map.values().parallelStream().flatMap(c -> c.stream()).collect(Collectors.toList());
    }
    System.out.println(System.currentTimeMillis() - millis);

    System.out.println("2 foreach");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> mylist = new ArrayList<String>();
        map.values().forEach(mylist::addAll);
    }
    System.out.println(System.currentTimeMillis() - millis);        

    System.out.println("2 for");
    millis = System.currentTimeMillis();
    for (int i = 0; i < maxcounter; i++) {
        List<String> mylist = new ArrayList<String>();
        for (List<String> item : map.values()) {
            mylist.addAll(item);
        }
    }
    System.out.println(System.currentTimeMillis() - millis);
Run Code Online (Sandbox Code Playgroud)

以下是结果:

1 stream flatmap
468
1 parallel stream flatmap
1529
1 foreach
140
1 for
172
2 stream flatmap
296
2 parallel stream flatmap
1482
2 foreach
156
2 for
141
Run Code Online (Sandbox Code Playgroud)

编辑2016-05-24(两年后):

在同一台机器上使用实际的Java 8版本(U92)运行相同的测试:

1 stream flatmap
313
1 parallel stream flatmap
3257
1 foreach
109
1 for
141
2 stream flatmap
219
2 parallel stream flatmap
3830
2 foreach
125
2 for
140
Run Code Online (Sandbox Code Playgroud)

似乎存在流的顺序处理加速和并行流的更大开销.

编辑2018-10-18(两年后):

在同一台机器上使用现在的Java 10版本(10.0.2):

1 stream flatmap
393
1 parallel stream flatmap
3683
1 foreach
157
1 for
175
2 stream flatmap
243
2 parallel stream flatmap
5945
2 foreach
128
2 for
187
Run Code Online (Sandbox Code Playgroud)

并行流的开销似乎更大.

  • 虽然实际上长了几个字符,但写'flatMap(Collections :: stream)`在样式上可能更适合`flatMap(c - > c.stream())`. (8认同)
  • 它是`Collection :: stream`,使用`Collections`在我的测试中不会做. (4认同)

Jos*_*h M 54

如果您使用的是Java 8,则可以执行以下操作:

someMap.values().forEach(someList::addAll);
Run Code Online (Sandbox Code Playgroud)

  • 如果我没有错,实际上不建议这样做 - https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html请参阅side -effects部分.>一般来说,不鼓励行为参数对流操作的副作用,因为它们通常会导致无意中违反无国籍要求,以及其他线程安全危险.所以在这种情况下最好使用`Collector.toList()` (5认同)
  • @AntonBalaniuc,您引用了“stream”文档,但这里没有使用它。`list.stream.forEach` != `list.forEach` (2认同)

use*_*306 35

当搜索"java 8 flatten"时,这是唯一提到的.而且它也不是要平整流.所以非常好,我就把它放在这里

.flatMap(Collection::stream)
Run Code Online (Sandbox Code Playgroud)

我也很惊讶没有人给出原始问题的并发java 8答案

.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
Run Code Online (Sandbox Code Playgroud)

  • 我相信`.collect(ArrayList :: new,ArrayList :: addAll,ArrayList :: addAll);`是正确的答案.`flatMap()`在这种情况下没用.如果在获取流之前需要在参数上调用另一个方法(即调用`stream()`方法),`flatMap()`会很有用.但是,在这里,我们已经引用了一个可以直接检索流的对象. (3认同)

orb*_*ish 8

同事建议:

listOfLists.stream().flatMap(e -> e.stream()).collect(Lists.toList())
Run Code Online (Sandbox Code Playgroud)

我比forEach()更喜欢它.

  • 您可以使用List :: stream的方法引用替换e - > e.stream().应该快一点. (2认同)

Cra*_*lin 7

如果您正在使用Eclipse Collections,则可以使用Iterate.flatten().

MutableMap<String, MutableList<String>> map = Maps.mutable.empty();
map.put("Even", Lists.mutable.with("0", "2", "4"));
map.put("Odd", Lists.mutable.with("1", "3", "5"));
MutableList<String> flattened = Iterate.flatten(map, Lists.mutable.empty());
Assert.assertEquals(
    Lists.immutable.with("0", "1", "2", "3", "4", "5"),
    flattened.toSortedList());
Run Code Online (Sandbox Code Playgroud)

flatten()是更一般的RichIterable.flatCollect()的特例.

MutableList<String> flattened = 
    map.flatCollect(x -> x, Lists.mutable.empty());
Run Code Online (Sandbox Code Playgroud)

注意:我是Eclipse Collections的提交者.


Jon*_*oni 5

不,没有更短的方法.你必须使用循环.

2014年4月更新: Java 8终于问世了.在新版本中,您可以使用该Iterable.forEach方法遍历集合而不使用显式循环.

2017年11月更新:在寻找现代解决方案时,偶然发现了这个问题.结束reduce:

someMap.values().stream().reduce(new ArrayList(), (accum, list) -> {
    accum.addAll(list);
    return accum;
}):
Run Code Online (Sandbox Code Playgroud)

这避免了依赖于forEach(someList::addAll)开销的可变外部状态flatMap(List::stream).