Java 8에서 List List를 List로 바꾸려면 어떻게해야합니까?


532

내가있는 경우 Java 8의 기능을 사용하여 동일한 반복 순서로 모든 객체를 포함 List<List<Object>>하는로 변환 할 수 List<Object>있습니까?

답변:


950

당신이 사용할 수있는 flatMap하나의 스트림에 (스트림으로 변환 후) 내부 목록을 평평하게 한 다음리스트로 결과를 수집 :

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());

11
@arshajii true이지만 어떤 이유로 람다 식을 선호합니다. 아마도 나는 모양이 마음에 들지 않을 것이다 :::)
Eran

25
Class::method처음에는 조금 이상하게 느껴지지만 어떤 종류의 객체를 매핑하는지 선언하는 이점이 있습니다. 그것은 스트림에서 다른 방법으로 잃어버린 것입니다.
ArneHugo

2
그러나 List의 컨테이너를 원할 경우 람다 (l-> l.myList.stream ())를 사용해야합니다.
Myoch

2
명시 적 캐스트를 수행해야하는 경우 (예 : 프리미티브의 배열을 List로 지정) 람다도 필요할 수 있습니다.
Michael Fulton

52

flatmap 더 낫지 만 동일한 것을 달성하는 다른 방법이 있습니다

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

List<Object> collect = 
      listOfList.stream()
                .collect(ArrayList::new, List::addAll, List::addAll);

37

flatMap메소드 Stream는 확실히 해당 목록을 평평하게 할 수 있지만 Streamelement에 대한 객체를 만든 다음 aStream 결과에 대한 .

모든 Stream객체 가 필요하지는 않습니다 . 다음은 작업을 수행하는 간단하고 간결한 코드입니다.

// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);

Listis 이므로이 Iterable코드 는에서 상속 된 메소드 (Java 8 기능)를 호출 합니다forEachIterable .

Iterable모든 요소가 처리되거나 작업에서 예외가 발생할 때까지 의 각 요소에 대해 지정된 작업을 수행합니다 . 해당 순서가 지정된 경우 반복 순서대로 조치가 수행됩니다.

그리고 ListIterator 순서대로 반환 항목.

의 경우 Consumer,이 코드는 List.addAll내부 목록 요소를 순차적으로 추가 하기 위해 메소드 참조 (Java 8 기능)를 Java 8 이전 메소드 로 전달합니다.

지정된 컬렉션의 모든 요소를 ​​지정된 컬렉션의 반복자에 의해 반환되는 순서대로이 목록의 끝에 추가합니다 (선택적 작업).


3
불필요한 할당을 피하는 좋은 대안 제안. 더 큰 컬렉션으로 작업 할 때이 힙의 최대 사용량을보고 서로 비교하는 방법을 보는 것이 흥미로울 것입니다.
Per Lundberg

12

Eclipse CollectionsflatCollect()패턴을 사용할 수 있습니다 .

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에 기고자입니다.


21
Java 8에서 기능을 제공 할 때 왜 타사 종속성을 사용합니까?
saw303

2
Eclipse Collections API는 콜렉션 자체에 있으므로 코드가 간결합니다.이 경우 주요 이유 중 하나입니다.
Nikhil Nanivadekar

11

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

11

같은 시나리오를 하나 더 설명하고 싶습니다 List<Documents>.이 목록에는 List<Excel>,, List<Word>등 의 다른 문서 목록이 몇 개 더 List<PowerPoint>있습니다. 그래서 구조는

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.
 }

코딩하는 동안 누군가의 문제를 해결할 수 있기를 바랍니다.


1

우리는 이것을 위해 flatmap을 사용할 수 있습니다. 아래 코드를 참조하십시오 :

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

1

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 문을 갖는 것과 유사합니다.


1

메소드를 변환 List<List>합니다 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]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.