Java 展平集合

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18290935/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:07:30  来源:igfitidea点击:

Flattening a collection

javacollections

提问by Tony Ennis

Say I have a Map<? extends Object, List<String>>

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

I can get the values of the map easily enough, and iterate over it to produce a single List<String>.

我可以很容易地获取地图的值,并对其进行迭代以生成单个List<String>.

   for (List<String> list : someMap.values()) {
        someList.addAll(list);
    }

Is there a way to flatten it in one shot?

有没有办法一口气把它弄平?

  List<String> someList = SomeMap.values().flatten();

采纳答案by Josh M

If you are using Java 8, you could do something like this:

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

someMap.values().forEach(someList::addAll);

回答by Joni

No, there is no shorter method. You have to use a loop.

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

Update Apr 2014:Java 8 has finally come out. In the new version you can use the Iterable.forEachmethod to walk over a collection without using an explicit loop.

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

Update Nov 2017:Found this question by chance when looking for a modern solution. Ended up going with reduce:

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

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

This avoids depending on mutable external state of forEach(someList::addAll)the overhead of flatMap(List::stream).

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

回答by David

If you just want to iterate through values, you can avoid all these addAll methods.

如果您只想遍历值,则可以避免所有这些 addAll 方法。

All you have to do is write a class that encapsulates your Map, and that implements the Iterator :

您所要做的就是编写一个封装 Map 并实现 Iterator 的类:

public class ListMap<K,V> implements Iterator<V>
{
  private final Map<K,List<V>> _map;
  private Iterator<Map.Entry<K,List<V>>> _it1 = null;
  private Iterator<V> _it2 = null;

  public ListMap(Map<K,List<V>> map)
  {
    _map = map;
    _it1 = map.entrySet().iterator(); 
    nextList();
  }

  public boolean hasNext()
  {
    return _it2!=null && _it2.hasNext();
  }

  public V next()
  {
    if(_it2!=null && _it2.hasNext())
    {
      return _it2.next();
    }
    else
    {
      throw new NoSuchElementException();
    }
    nextList();
  } 

  public void remove()
  {
    throw new NotImplementedException();
  }

  private void nextList()
  {
    while(_it1.hasNext() && !_it2.hasNext())
    {
      _it2 = _it1.next().value();
    }
  }
}

回答by Craig P. Motlin

If you're using Eclipse Collections, you can use Iterate.flatten().

如果您使用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());

flatten()is a special case of the more general RichIterable.flatCollect().

flatten()是更通用的RichIterable.flatCollect() 的特例

MutableList<String> flattened = 
    map.flatCollect(x -> x, Lists.mutable.empty());

Note: I am a committer for Eclipse Collections.

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

回答by wumpz

Using Java 8 and if you prefer not to instantiate a Listinstance by yourself, like in the suggested (and accepted) solution

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

someMap.values().forEach(someList::addAll);

You could do it all by streaming with this statement:

您可以通过使用以下语句进行流式传输来完成所有操作:

List<String> someList = map.values().stream().flatMap(c -> c.stream()).collect(Collectors.toList());

By the way it should be interesting to know, that on Java 8 the accepted version seems to be indeed the fastest. It has about the same timing as a

顺便说一下,知道在 Java 8 上接受的版本似乎确实是最快的应该很有趣。它的时间与

for (List<String> item : someMap.values()) ...

and is a way faster than the pure streaming solution. Here is my little testcode. I explicitly don't name it benchmark to avoid the resulting discussion of benchmark flaws. ;) I do every test twice to hopefully get a full compiled version.

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

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

And here are the results:

结果如下:

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

Edit 2016-05-24 (two years after):

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

Running the same test using an actual Java 8 version (U92) on the same machine:

在同一台机器上使用实际的 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

It seems that there is a speedup for sequential processing of streams and an even larger overhead for parallel streams.

流的顺序处理似乎有加速,而并行流的开销更大。

Edit 2018-10-18 (four years after):

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

Using now Java 10 version (10.0.2) on the same machine:

现在在同一台机器上使用 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

The overhead for parallel streaming seems to be larger.

并行流的开销似乎更大。

Edit 2020-05-22 (six years after):

编辑 2020-05-22(六年后):

Using now Java 14 version (14.0.0.36) on the same machine:

现在在同一台机器上使用 Java 14 版本 (14.0.0.36):

1 stream flatmap
299
1 parallel stream flatmap
3209
1 foreach
202
1 for
170
2 stream flatmap
178
2 parallel stream flatmap
3270
2 foreach
138
2 for
167

It should be noted, that this was done on a different machine (but I think comparable). The parallel streaming overhead seems to be considerably smaller than before.

应该注意的是,这是在不同的机器上完成的(但我认为具有可比性)。并行流开销似乎比以前小得多。

回答by user2418306

When searching for "java 8 flatten" this is the only mentioning. And it's not about flattening stream either. So for great good I just leave it here

在搜索“java 8 flatten”时,这是唯一提到的。这也不是关于扁平化流。所以为了好我就把它留在这里

.flatMap(Collection::stream)

I'm also surprised no one has given concurrent java 8 answer to original question which is

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

.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);

回答by Guy Grin

A nice solution for the subcase of a Map of Maps is to store, if possible, the data in Guava's Table.

对于 Map of Maps 的子案例,一个不错的解决方案是,如果可能,将数据存储在 Guava 的Table.

https://github.com/google/guava/wiki/NewCollectionTypesExplained#table

https://github.com/google/guava/wiki/NewCollectionTypesExplained#table

So for instance a Map<String,Map<String,String>>is replaced by Table<String,String,String>which is already flattend. In fact, the docs say that HashBasedTable, Table's Hash implementation, is essentially backed by a HashMap<R, HashMap<C, V>>

因此,例如 aMap<String,Map<String,String>>被替换为Table<String,String,String>which 已经变平了。事实上,文档说HashBasedTable,Table的哈希实现,本质上是由一个HashMap<R, HashMap<C, V>>

回答by orbfish

Suggested by a colleague:

同事推荐:

listOfLists.stream().flatMap(e -> e.stream()).collect(Lists.toList())

I like it better than forEach().

我比 forEach() 更喜欢它。

回答by Leo Duarte

Flatten on a function:

在函数上展平:

    private <A, T> List<T> flatten(List<A> list, Function<A, List<T>> flattenFn) {
        return list
                .stream()
                .map(flattenFn)
                .flatMap(Collection::stream)
                .collect(Collectors.toUnmodifiableList());
    }