Program Tip

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

programtip 2020. 12. 6. 22:00
반응형

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


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

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

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


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

objectA.removeIf (x-> 조건);

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

출력 : ABC


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

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

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

API 참조 : removeIf 문서

가정은 : producersProcedureActiveA는List

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


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

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 될 수 있습니다.

직접적인 해결책은 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)작업이 인덱스에서 직접 작동합니다.


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

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

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

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

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

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


함께 이클립스 컬렉션 당신이 사용할 수 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 컬렉션의 커미터입니다.


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


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

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"]

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

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

It lets remove the object using remove(int) that do not traverse again the list (as suggested by @Tunaki) and it lets return the removed object to the function caller.

I read your answers that suggest me to choose safe methods like ifPresent instead of get but I do not find a way to use them in this scenario.

Are there any important drawback in this kind of solution?

Edit following @Holger advice

This should be the function I needed

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

When we want to get multiple elements from a List into a new list (filter using a predicate) and remove them from the existing list, I could not find a proper answer anywhere.

Here is how we can do it using Java Streaming API partitioning.

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

This way you effectively remove the filtered elements from the original list and add them to a new list.

Refer the 5.2. Collectors.partitioningBy section of this article.


the task is: get ✶and✶ remove element from list

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

참고URL : https://stackoverflow.com/questions/35701337/java-8-lambda-get-and-remove-element-from-list

반응형