Java 8의 java.util.stream.Stream에서 목록 검색


443

컬렉션을 쉽게 필터링하기 위해 Java 8 람다로 놀고있었습니다. 그러나 나는 동일한 진술 내에서 결과를 새로운 목록으로 검색하는 간결한 방법을 찾지 못했습니다. 지금까지 가장 간결한 접근 방식은 다음과 같습니다.

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = new ArrayList<>();
sourceLongList.stream().filter(l -> l > 100).forEach(targetLongList::add);

그물에있는 예제는 새로운 결과 목록을 생성하지 않고 멈추기 때문에 내 질문에 대답하지 못했습니다. 더 간결한 방법이 있어야합니다. 나는이 것을 예상했을 것이다 Stream클래스 등의 방법이있다 toList(), toSet()...

targetLongList세 번째 줄 에서 변수 를 직접 할당 할 수 있는 방법이 있습니까?


7
sourceLongList나중에 필요하지 않은 경우 Collection.removeIf(…)편의를 위해 존재 합니다.
Holger

2
이건 어때요? List<Long> targetLongList = sourceLongList.stream().collect(Collectors.toList());
leeyuiwah

답변:


609

스트림이 순차적으로 유지되는 경우 가장 간단한 방법 일 수 있습니다 forEach.

[나중에 편집 : sequence ()에 대한 호출이 필요한 이유 forEach(targetLongList::add)는 스트림이 병렬 인 경우 코드 ( )가 고르지 않기 때문 입니다. 그럼에도 불구하고, 그것은 forEach명백히 비결정론 적이며 의도 된 효과를 달성하지 못할 것 입니다. 순차 스트림에서도 요소 처리 순서가 보장되지는 않습니다. forEachOrdered올바른 주문을 위해 사용해야 합니다. Stream API 디자이너의 의도는 아래와 같이이 상황에서 수집기를 사용하는 것입니다.]

대안은

targetLongList = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(Collectors.toList());

10
추가 : 정적 가져 오기를 사용하면이 코드가 조금 더 짧고 명확하고 예쁘게된다고 생각합니다 toList. 파일 가져 오기 중에 다음을 배치하면 static import java.util.stream.Collectors.toList;됩니다.. 그런 다음 수집 호출은 단지 읽습니다 .collect(toList()).
Lii

4
Eclipse에서 IDE가 메소드에 대한 정적 가져 오기를 추가하도록 할 수 있습니다. 환경 설정 -> Java- > 편집기 -> 컨텐츠 지원 -> 즐겨 찾기에Collectors 클래스를 추가하면 됩니다. 그런 다음 IDE를 채우고 정적 가져 오기를 추가하려면 적중 Ctr + Space 만 입력하면됩니다 . toLitoList
Lii

3
명심해야 할 것은, IntStream그리고 거의 그렇지 않은 다른 것들 Stream에는 collect(Collector)메소드 가 없기 때문에 IntStream.boxed()그것들을 정기적으로 변환하기 위해 호출해야한다는 것 Stream입니다. 그런 다음 다시 원할 수도 있습니다 toArray().
돌연변이 밥

우리가 사용하는 이유 sequential()이전 forEach또는 사용 'forEachOrdered`
amarnath harish

1
@amarnathharish forEach는 병렬 스트림에 대한 연산 실행 순서를 보장하지 않기 때문입니다. JavaDoc은 "이 작업의 동작은 명백히 비 결정적입니다. 병렬 스트림 파이프 라인의 경우이 작업은 스트림의 발생 순서를 존중한다고 보장하지는 않습니다. 병렬 처리의 이점을 희생시킬 것입니다." (이 인용문의 첫 번째 문장은 실제로는 순서대로 유지되지만 순차적 스트림에 대해서는 순서가 보장되지 않음을 의미합니다.)
Maurice Naftalin

183

업데이트 :

또 다른 방법은 다음을 사용하는 것입니다 Collectors.toList.

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toList());

이전 솔루션 :

또 다른 방법은 다음을 사용하는 것입니다 Collectors.toCollection.

targetLongList = 
    sourceLongList.stream().
    filter(l -> l > 100).
    collect(Collectors.toCollection(ArrayList::new));

60
그러나 이것은 특정 List 구현을 원하는 경우에 유용합니다.
orbfish 2016 년

1
인터페이스에 대한 코드 작성을 권장하지만 구체적인 구현에 대해 코드를 작성해야하는 경우 (GWT가되는 경우)가 있습니다 (모든 List 구현을 javascript로 컴파일하여 제공하지 않는 한).
Eduard Korenschi

3
Collectors::toListjavadoc 의이 메소드에 대한 또 다른 전문가 : "반환 된 List의 유형, 변경 가능성, 직렬화 가능성 또는 스레드 안전성에 대한 보장은 없습니다. 리턴 된 List에 대한 추가 제어가 필요한 경우을 사용하십시오 toCollection(Supplier)."
Charles Wood

12

필자가 원하는 ArrayList때에 수집기를 반환하는 util 메소드를 사용하고 싶습니다.

사용하는 솔루션 Collectors.toCollection(ArrayList::new)이 이러한 일반적인 작업에는 너무 시끄럽다고 생각합니다 .

예:

ArrayList<Long> result = sourceLongList.stream()
    .filter(l -> l > 100)
    .collect(toArrayList());

public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
    return Collectors.toCollection(ArrayList::new);
}

이 답변을 통해 사용자 지정 수집기를 만들고 사용하는 것이 얼마나 간단한 지 보여주고 싶습니다. 이는 일반적으로 매우 유용합니다.


List <Long>으로 결과를 선언하면이 util 메소드를 사용할 필요가 없습니다. Collectors.toList가 수행합니다. 또한 인터페이스 대신 특정 클래스를 사용하는 것은 코드 냄새입니다.
Lluis Martinez

1
@LluisMartinez : "Collectors.toList가 수행합니다." : 아니오, 많은 상황에서 아닙니다. toList예를 들어 프로그램에서 나중에 목록을 수정하려는 경우 사용하지 않는 것이 좋습니다 . toList문서는 이 말한다 : "유형, 가변성, 직렬화, 또는 목록의 스레드 안전성 반환에 대한 보장은 없습니다; 반환 된 목록보다 강력하게 제어 할 필요가있는 경우 사용 toCollection." . 내 대답은 일반적인 경우에보다 편리하게 수행 할 수있는 방법을 보여줍니다.
Lii

ArrayList를 구체적으로 작성하려면 괜찮습니다.
Lluis Martinez

5

프리미티브 배열이있는 경우 Eclipse Collections 에서 사용 가능한 프리미티브 콜렉션을 사용할 수 있습니다 .

LongList sourceLongList = LongLists.mutable.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
LongList targetLongList = sourceLongList.select(l -> l > 100);

sourceLongList를 List다음 에서 변경할 수없는 경우 :

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = 
    ListAdapter.adapt(sourceLongList).select(l -> l > 100, new ArrayList<>());

사용하려는 경우 LongStream:

long[] sourceLongs = new long[]{1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L};
LongList targetList = 
    LongStream.of(sourceLongs)
    .filter(l -> l > 100)
    .collect(LongArrayList::new, LongArrayList::add, LongArrayList::addAll);

참고 : 저는 Eclipse Collections에 기고자입니다.


4

좀 더 효율적인 방법 (소스 목록 작성 및 필터에 의한 자동 우편함 제거) :

List<Long> targetLongList = LongStream.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L)
    .filter(l -> l > 100)
    .boxed()
    .collect(Collectors.toList());


1

써드 파티 라이브러리를 사용하지 않아도 AOL의 cyclops-react lib (제공자 인 공개)에는 List를 포함한 모든 JDK 콜렉션 유형에 대한 확장이 있습니다 . ListX 인터페이스는 java.util.List를 확장하고 필터를 포함하여 많은 유용한 연산자를 추가합니다.

간단히 쓸 수 있습니다.

ListX<Long> sourceLongList = ListX.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
ListX<Long> targetLongList = sourceLongList.filter(l -> l > 100);

ListX는 기존 List에서 생성 할 수도 있습니다 (ListX.fromIterable을 통해)


1

LongStream 클래스와 IntStream 및 DoubleStream 클래스에 의해 제공되는 다른 collect 메소드 변형이 있습니다.

<R> R collect(Supplier<R> supplier,
              ObjLongConsumer<R> accumulator,
              BiConsumer<R,R> combiner)

이 스트림의 요소에 대해 변경 가능한 축소 작업을 수행합니다. 변경 가능한 축소는 감소 된 값이 ArrayList와 같은 변경 가능한 결과 컨테이너 인 요소이며, 결과를 바꾸지 않고 결과의 상태를 업데이트하여 요소를 통합합니다. 결과는 다음과 같습니다.

R result = supplier.get();
  for (long element : this stream)
       accumulator.accept(result, element);
  return result;

reduce (long, LongBinaryOperator)와 같이 수집 작업을 추가 동기화없이 병렬화 할 수 있습니다. 이것은 터미널 작업입니다.

이 수집 방법으로 귀하의 질문에 대한 답변은 다음과 같습니다.

    LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, (list, value) -> list.add(value)
    , (list1, list2) -> list1.addAll(list2));

아래는 꽤 똑똑하지만 이해하기 까다로운 메소드 참조 변형입니다.

     LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
    .collect(ArrayList::new, List::add , List::addAll);

다음은 HashSet 변형입니다.

     LongStream.of(1L, 2L, 3L, 3).filter(i -> i > 2)
     .collect(HashSet::new, HashSet::add, HashSet::addAll);

마찬가지로 LinkedList 변형은 다음과 같습니다.

     LongStream.of(1L, 2L, 3L, 3L)
     .filter(i -> i > 2)
     .collect(LinkedList::new, LinkedList::add, LinkedList::addAll);

0

누군가 (나 같은) 누군가 기본 유형 대신 객체를 다루는 방법을 찾고 있다면 mapToObj()

String ss = "An alternative way is to insert the following VM option before "
        + "the -vmargs option in the Eclipse shortcut properties(edit the "
        + "field Target inside the Shortcut tab):";

List<Character> ll = ss
                        .chars()
                        .mapToObj(c -> new Character((char) c))
                        .collect(Collectors.toList());

System.out.println("List type: " + ll.getClass());
System.out.println("Elem type: " + ll.get(0).getClass());
ll.stream().limit(50).forEach(System.out::print);

인쇄물:

List type: class java.util.ArrayList
Elem type: class java.lang.Character
An alternative way is to insert the following VM o

0
String joined = 
                Stream.of(isRead?"read":"", isFlagged?"flagged":"", isActionRequired?"action":"", isHide?"hide":"")
                      .filter(s -> s != null && !s.isEmpty())
                      .collect(Collectors.joining(","));

0

다음은 AbacusUtil의 코드입니다 .

LongStream.of(1, 10, 50, 80, 100, 120, 133, 333).filter(e -> e > 100).toList();

공개 : 저는 AbacusUtil의 개발자입니다.


LongStream 클래스에 toList 메소드가 없습니다. 이 코드를 실행할 수 있습니까?
Vaneet Kataria

@VaneetKataria 시도 com.landawn.abacus.util.stream.LongStream또는 LongStreamExAbacusUtil
user_3380739

0

아래와 같이 코드를 다시 작성할 수 있습니다.

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = sourceLongList.stream().filter(l -> l > 100).collect(Collectors.toList());

입력 해 주셔서 감사합니다. 그러나 변경 한 내용과 질문과 얼마나 관련이 있는지 설명하십시오.
B--rian

먼저 필터를 사용하여 ArrayList를 변환하여 필요한 데이터를 필터링합니다. 마지막으로 targetLongList라는 새 목록에서 데이터를 수집하기 위해 java 8 스트림의 collect 메소드를 사용했습니다.
Pratik Pawar

0

변경 가능한 목록에서 수집하려면 다음을 수행하십시오.

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toList());

변경 불가능한 목록으로 수집하려면 다음을 수행하십시오.

targetList = sourceList.stream()
                       .filter(i -> i > 100) //apply filter
                       .collect(Collectors.toUnmodifiableList());

JavaDoccollect 에서 설명 :

수집기를 사용하여이 스트림의 요소에 대해 변경 가능한 축소 작업을 수행합니다. 수집기는 수집 (인수자, 공급 업체, BiConsumer, BiConsumer)에 인수로 사용 된 기능을 캡슐화하여 수집 전략을 재사용하고 다중 레벨 그룹화 또는 파티셔닝과 같은 수집 작업의 구성을 허용합니다. 스트림이 병렬이고 수집기가 동시이고 스트림이 정렬되지 않거나 수집기가 정렬되지 않은 경우 동시 축소가 수행됩니다 (동시 축소에 대한 자세한 내용은 수집기 참조).

이것은 터미널 작업입니다.

병렬로 실행될 때, 가변 데이터 구조의 격리를 유지하기 위해 다수의 중간 결과가 인스턴스화되고, 채워지고, 병합 될 수있다. 따라서 스레드로부터 안전하지 않은 데이터 구조 (예 : ArrayList)와 병렬로 실행될 때에도 병렬 감소를 위해 추가 동기화가 필요하지 않습니다.


-3

사용하지 않으면 parallel()작동합니다.

List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);

List<Long> targetLongList =  new ArrayList<Long>();

sourceLongList.stream().peek(i->targetLongList.add(i)).collect(Collectors.toList());

collect ()가 스트림을 구동하는 데만 사용되어 각 항목에서 peek () 후크가 호출되는 것을 좋아하지 않습니다. 터미널 작업의 결과는 무시됩니다.
Daniel K.

호출 collect하고 반환 값을 저장하지 않는 것이 매우 이상 합니다. 이 경우 forEach대신 사용할 수 있습니다 . 그러나 그것은 여전히 ​​나쁜 해결책입니다.
Lii

1
이런 방식으로 peek ()를 사용하는 것은 반 패턴입니다.
Grzegorz Piwowarek

Stream Java docs peek 메소드는 디버깅 목적으로 만 사용해야하며 디버깅 이외의 처리에는 사용하지 않아야합니다.
Vaneet Kataria
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.