IT TIP

컬렉션 병합

itqueen 2020. 12. 13. 11:33
반응형

컬렉션 병합


내가 가지고 있다고 Map<? extends Object, List<String>>

맵의 값을 쉽게 얻을 수 있으며이를 반복하여 단일 List<String>.

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

한 번에 평면화하는 방법이 있습니까?

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

Java 8을 사용하는 경우 다음과 같이 할 수 있습니다.

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

Java 8을 사용 List하고 제안 된 (및 허용 된) 솔루션 에서처럼 인스턴스를 직접 인스턴스화하지 않으려는 경우

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

다음 문으로 스트리밍하여 모든 작업을 수행 할 수 있습니다.

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

그건 그렇고, Java 8에서 허용되는 버전이 실제로 가장 빠르다는 것을 아는 것은 흥미로울 것입니다. 그것은 거의 같은 타이밍을 가지고

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

순수한 스트리밍 솔루션보다 빠른 방법입니다. 여기 내 작은 테스트 코드가 있습니다. 벤치 마크 결함에 대한 논의를 피하기 위해 벤치 마크 이름을 명시 적으로 지정하지 않습니다. ;) 완전한 컴파일 버전을 얻기 위해 모든 테스트를 두 번 수행합니다.

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

결과는 다음과 같습니다.

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

2016-05-24 수정 (2 년 후) :

동일한 시스템에서 실제 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

스트림의 순차 처리 속도가 빨라지고 병렬 스트림의 경우 더 큰 오버 헤드가있는 것 같습니다.

2018-10-18 수정 (2 년 후) :

동일한 시스템에서 현재 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

병렬 스트리밍의 오버 헤드가 더 큰 것 같습니다.


"java 8 flatten"을 검색 할 때 이것이 유일한 언급입니다. 그리고 그것은 평탄화에 관한 것도 아닙니다. 그래서 대단한 일을 위해 여기에 남겨 둡니다.

.flatMap(Collection::stream)

나는 또한 아무도 원래 질문에 대한 동시 자바 8 답변을주지 않았다는 것에 놀랐습니다.

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

동료의 제안 :

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

forEach ()보다 좋습니다.


당신이 사용하는 경우 이클립스 컬렉션 , 당신이 사용할 수있는 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().

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

Note: I am a committer for Eclipse Collections.


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.forEach method to walk over a collection without using an explicit loop.

Update Nov 2017: Found this question by chance when looking for a modern solution. Ended up going with 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).


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

All you have to do is write a class that encapsulates your Map, and that implements the 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();
    }
  }
}

A nice solution for the subcase of a Map of Maps is to store, if possible, the data in Guava's 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>>

참고URL : https://stackoverflow.com/questions/18290935/flattening-a-collection

반응형