Java 8 람다 목록에서 요소 가져 오기 및 제거


88

요소의 목록을 감안할 때, 나는 주어진 속성 요소를 싶어 하고 목록에서 제거합니다. 내가 찾은 최고의 솔루션은 다음과 같습니다.

ProducerDTO p = producersProcedureActive
                .stream()
                .filter(producer -> producer.getPod().equals(pod))
                .findFirst()
                .get();
producersProcedureActive.remove(p);

람다 식에서 get과 remove를 결합 할 수 있습니까?


9
이것은 실제로 루프와 반복자를 대신 사용하는 고전적인 경우처럼 보입니다.
chrylis -cautiouslyoptimistic-

1
@chrylis 나는 친절하게 동의하지 않는다;) 우리는 명령형 프로그래밍에 너무 익숙해 져서 다른 방법으로는 너무 이국적으로 들립니다. 현실이 반대라면 우리는 함수형 프로그래밍에 매우 익숙하고 새로운 명령 패러다임이 Java에 추가됩니다. 이것이 스트림, 술어 및 선택 사항의 전형적인 경우라고 말 하시겠습니까?
fps

9
get()여기에 전화하지 마세요 ! 비어 있는지 여부를 알 수 없습니다. 요소가 없으면 예외가 발생합니다. 대신 ifPresent, orElse, orElseGet 또는 orElseThrow와 같은 안전한 방법 중 하나를 사용하십시오.
Brian Goetz

@FedericoPeraltaSchaffner 아마도 취향의 문제 일 것입니다. 그러나 나는 또한 두 가지를 결합하지 않을 것을 제안 할 것입니다. 스트림을 사용하여 값을 얻는 코드를 볼 때 일반적으로 스트림의 작업에 부작용이 없다고 가정합니다. 요소 제거를 혼합하면 독자가 오해 할 수있는 코드가 발생할 수 있습니다.
Matthias Wimmer

명확하게하기 위해 :의 모든 요소를 ​​제거 하시겠습니까 list, Predicate아니면 첫 번째 요소 만 제거 하시겠습니까?
Kedar Mhaswade

답변:


148

목록에서 요소를 제거하려면

objectA.removeIf(x -> conditions);

예 :

objectA.removeIf(x -> blockedWorkerIds.contains(x));

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

str1.removeIf(x -> str2.contains(x)); 

str1.forEach(System.out::println);

출력 : A B C


내가 가장 좋은 대답은이 좋을 것
세르게이 Pauk에게

1
이것은 매우 깔끔합니다. 자체 객체에 대해 수행되지 않은 경우 equals / hashCode를 구현해야 할 수 있습니다. (이 예제에서는 기본적으로 문자열이 사용됩니다).
bram000

15
IMHO 대답은 "get"부분을 고려하지 않습니다 removeIf. 컬렉션에서 요소를 제거하는 우아한 솔루션이지만 제거 된 요소를 반환하지는 않습니다.
Marco Stramezzi

Java ArrayList에서 객체를 제외하는 매우 간단한 모드입니다. 많이 Thkns. 나에게 잘 작동합니다.
Marcelo Rebouças

2
이것은 질문에 대한 답이 아닙니다. 요구 사항은 목록에서 요소를 제거하고 제거 된 항목 / 항목을 새 목록으로 가져 오는 것입니다.
Shanika Ediriweera

34

스레드가 꽤 오래되었지만 여전히 솔루션을 제공하는 것으로 생각됩니다 Java8.

removeIf기능을 사용하십시오 . 시간 복잡성은O(n)

producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));

API 참조 : removeIf 문서

가정은 : producersProcedureActiveA는List

참고 :이 접근 방식을 사용하면 삭제 된 항목을 유지할 수 없습니다.


목록에서 요소를 삭제하는 것 외에도 OP는 여전히 요소에 대한 참조를 원합니다.
eee

@eee : 지적 해 주셔서 감사합니다. OP의 원래 질문에서 그 부분을 놓쳤습니다.
asifsid88

참고로 조건과 일치하는 모든 항목이 제거됩니다. 그러나 영업 이익 (영업 이익은로 findFirst () 사용) 첫 번째 항목을 제거 할 필요가 보인다
nantitv

17

바닐라 자바 ​​반복기를 사용하여 작업을 수행하는 것이 좋습니다.

public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) {
    T value = null;
    for (Iterator<? extends T> it = collection.iterator(); it.hasNext();)
        if (test.test(value = it.next())) {
            it.remove();
            return value;
        }
    return null;
}

장점 :

  1. 분명하고 분명합니다.
  2. 일치하는 요소까지 한 번만 순회합니다.
  3. 지원 Iterable없이도 할 수 있습니다 (적어도 반복기에서 구현 하는 사람들 ) .stream()remove()

단점 :

  1. 단일 표현식으로 제자리에서 수행 할 수 없습니다 (보조 방법 또는 변수 필요).

에 관해서

람다 식에서 get과 remove를 결합 할 수 있습니까?

다른 답변은 가능하다는 것을 명확하게 보여 주지만

  1. 검색 및 제거는 목록을 두 번 탐색 할 수 있습니다.
  2. ConcurrentModificationException 반복되는 목록에서 요소를 제거 할 때 throw 될 수 있습니다.

4
이 솔루션을 좋아하지만 놓친 한 가지 심각한 단점이 있습니다. 많은 Iterable 구현에는 remove()UOE를 던지는 메서드가 있습니다. (JDK 모음에 대한 안 사람은 물론,하지만 난 "로 Iterable에 작품"말의 불공평 생각합니다.)
브라이언 게츠

일반적으로 요소가 제거 될 수 있다면 반복자에 의해
Vasily Liaskovsky

5
가정 할 수는 있지만 수백 개의 반복기 구현을 살펴보면 잘못된 가정이 될 것입니다. (난 아직도 접근 방식처럼, 그냥 이상 판매 당신이있어.)
브라이언 게츠

2
@Brian Goetz :의 default구현은 removeIf동일한 가정을하지만, 물론 정의가 Collection아니라 Iterable...
Holger

13

직접적인 해결책은 ifPresent(consumer)에서 반환 된 Optional 을 호출 하는 것 findFirst()입니다. 이 소비자는 선택 사항이 비어 있지 않을 때 호출됩니다. 또한 현재 코드 에서처럼 find 작업이 빈 옵션을 반환하면 예외가 발생하지 않습니다. 대신 아무 일도 일어나지 않을 것입니다.

당신이 삭제 된 값을 반환 할 경우, 수 호출의 결과로 :mapOptionalremove

producersProcedureActive.stream()
                        .filter(producer -> producer.getPod().equals(pod))
                        .findFirst()
                        .map(p -> {
                            producersProcedureActive.remove(p);
                            return p;
                        });

그러나 remove(Object)작업은 제거 할 요소를 찾기 위해 목록을 다시 탐색합니다. 와 같이 임의 액세스 권한이있는 목록이있는 경우 목록 ArrayList의 인덱스에 대해 Stream을 만들고 술어와 일치하는 첫 번째 인덱스를 찾는 것이 좋습니다.

IntStream.range(0, producersProcedureActive.size())
         .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
         .boxed()
         .findFirst()
         .map(i -> producersProcedureActive.remove((int) i));

이 솔루션을 사용하면 remove(int)작업이 인덱스에서 직접 작동합니다.


3
이것은 연결된 목록의 병리학입니다.
chrylis -cautiouslyoptimistic-

1
@chrylis 인덱스 솔루션은 실제로 될 것입니다. 목록 구현에 따라 하나는 다른 것보다 선호합니다. 약간의 편집을했습니다.
Tunaki

1
@chrylis : LinkedList최소한 두 번 통과하지 않는 솔루션이 없기 때문에 스트림 API를 사용해서는 안됩니다. 그러나 나는 연결 목록의 학문적 이점이 실제 오버 헤드를 보상 할 수있는 실제 시나리오를 모릅니다. 따라서 간단한 해결책은 LinkedList.
Holger

2
오 너무 많은 편집… 이제 첫 번째 솔루션은 제거 할 요소 가 있는지 여부를 알려주 는 remove(Object)것만 반환 하기 때문에 제거 된 요소를 제공 boolean하지 않습니다.
Holger

3
@Marco Stramezzi : 안타깝게도 설명이 삭제되었습니다. 없이 boxed()당신이를 얻을 수 OptionalInt있는 유일한 수 map에서 할 intint. 와 달리 방법 IntStream이 없습니다 mapToObj. 와 함께 boxed(), 당신은 얻을 것이다 Optional<Integer>할 수있는 map즉, 임의의 객체에의 ProducerDTO에 의해 반환을 remove(int). 에서 주조 Integer로는 int사이에 명확하게 할 필요가 remove(int)remove(Object).
Holger

9

사용은 Java 8의 필터를 사용할 수 있으며 이전 목록을 변경하지 않으려면 다른 목록을 만들 수 있습니다.

List<ProducerDTO> result = producersProcedureActive
                            .stream()
                            .filter(producer -> producer.getPod().equals(pod))
                            .collect(Collectors.toList());

5

나는 이것이 인기없는 대답이 될 것이라고 확신하지만 작동합니다 ...

ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
            .stream()
            .filter(producer -> producer.getPod().equals(pod))
            .findFirst()
            .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}

p[0] 발견 된 요소를 보유하거나 널이됩니다.

여기서 "트릭" 은 사실상 최종적인 배열 참조를 사용 하지만 첫 번째 요소를 설정 하여 "효과적으로 최종"문제를 우회하는 것입니다.


1
그것은이 경우, 그것은 나쁜 것이 아니지만, 단지 호출 할 수있는 가능성에 비해 개선되지 .orElse(null)를 얻을 ProducerDTO또는 null...
홀거

이 경우, 그냥 가지고 쉬울 수 있습니다 .orElse(null)및이 if아니?
Tunaki

@Holger 그러나 어떻게 remove()사용하여 호출 할 수 orElse(null)있습니까?
보헤미안

1
결과를 사용하십시오. if(p!=null) producersProcedureActive.remove(p);그것은 여전히 ​​당신의 ifPresent호출 에서 람다 식보다 짧습니다 .
Holger 2016

@holger 나는 질문의 목표를 여러 문장을 피하는 것으로 해석했습니다. 즉, 한 줄짜리 솔루션
Bohemian

4

함께 이클립스 컬렉션 당신이 사용할 수 detectIndex와 함께 remove(int)어떤 java.util.List를합니다.

List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = Iterate.detectIndex(integers, i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

MutableListEclipse Collections 의 유형 을 사용하는 경우 detectIndex목록에서 직접 메소드를 호출 할 수 있습니다 .

MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = integers.detectIndex(i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

참고 : 저는 Eclipse 컬렉션의 커미터입니다.


2

우리가하고 싶을 때 (술어를 사용하여 필터) 새 목록으로 목록에서 여러 요소를 얻을 기존 목록에서 제거 , 나는 적절한 응답 어디를 찾을 수 없습니다.

다음은 Java Streaming API 파티셔닝을 사용하여 수행하는 방법입니다.

Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive
    .stream()
    .collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod)));

// get two new lists 
List<ProducerDTO> matching = classifiedElements.get(true);
List<ProducerDTO> nonMatching = classifiedElements.get(false);

// OR get non-matching elements to the existing list
producersProcedureActive = classifiedElements.get(false);

이렇게하면 원본 목록에서 필터링 된 요소를 효과적으로 제거하고 새 목록에 추가 할 수 있습니다.

5.2를 참조하십시오 . 이 기사Collectors.partitioningBy 섹션 .


1

다른 사람들이 제안했듯이 이것은 루프와 이터 러블의 사용 사례 일 수 있습니다. 제 생각에는 이것이 가장 간단한 접근 방식입니다. 목록을 제자리에서 수정하려는 경우 어쨌든 "실제"함수 프로그래밍으로 간주 할 수 없습니다. 그러나 Collectors.partitioningBy()조건을 충족하는 요소가있는 새 목록과 그렇지 않은 요소의 새 목록을 얻기 위해 사용할 수 있습니다 . 물론이 접근 방식을 사용하면 조건을 충족하는 여러 요소가있는 경우 첫 번째 요소뿐만 아니라 모든 요소가 해당 목록에 포함됩니다.


스트림을 필터링하고 결과를 새 목록으로 수집하는 것이 훨씬 더 좋습니다
fps

1

아래 논리는 원래 목록을 수정하지 않고 해결책입니다.

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

List<String> str3 = str1.stream()
                        .filter(item -> !str2.contains(item))
                        .collect(Collectors.toList());

str1 // ["A", "B", "C", "D"]
str2 // ["D", "E"]
str3 // ["A", "B", "C"]

0

내 초기 아이디어와 답변을 결합하여 내 질문에 대한 해결책으로 보이는 것에 도달했습니다.

public ProducerDTO findAndRemove(String pod) {
    ProducerDTO p = null;
    try {
        p = IntStream.range(0, producersProcedureActive.size())
             .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
             .boxed()
             .findFirst()
             .map(i -> producersProcedureActive.remove((int)i))
             .get();
        logger.debug(p);
    } catch (NoSuchElementException e) {
        logger.error("No producer found with POD [" + pod + "]");
    }
    return p;
}

그것은 사용하여 객체를 제거 할 수 있습니다 remove(int)(@Tunaki에 의해 제안)이하지 트래버스 목록을 다시 하고 는 함수 호출에 제거 된 개체를 반환 할 수 있습니다.

ifPresent대신 안전한 방법을 선택하도록 제안하는 답변을 읽었습니다.get 지만이 시나리오에서 사용할 방법을 찾지 못했습니다.

이러한 종류의 솔루션에 중요한 단점이 있습니까?

@Holger 조언에 따라 편집

이것은 내가 필요한 기능이어야합니다.

public ProducerDTO findAndRemove(String pod) {
    return IntStream.range(0, producersProcedureActive.size())
            .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))      
            .boxed()                                                                
            .findFirst()
            .map(i -> producersProcedureActive.remove((int)i))
            .orElseGet(() -> {
                logger.error("No producer found with POD [" + pod + "]"); 
                return null; 
            });
}

2
get예외를 사용 하고 포착 해서는 안됩니다 . 그것은 나쁜 스타일 일뿐만 아니라 성능을 저하시킬 수도 있습니다. 깨끗한 솔루션은 더 간단하다return /* stream operation*/.findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; });
홀거

0

작업은 : ✶ 가져 오기 ✶ 목록에서 요소 제거

p.stream().collect( Collectors.collectingAndThen( Collector.of(
    ArrayDeque::new,
    (a, producer) -> {
      if( producer.getPod().equals( pod ) )
        a.addLast( producer );
    },
    (a1, a2) -> {
      return( a1 );
    },
    rslt -> rslt.pollFirst()
  ),
  (e) -> {
    if( e != null )
      p.remove( e );  // remove
    return( e );    // get
  } ) );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.