web-dev-qa-db-ja.com

Java 8 lambdaリストから要素を取得および削除

要素のリストが与えられたら、特定のプロパティを持つ要素を取得しますandリストから削除します。私が見つけた最良の解決策は次のとおりです。

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

ラムダ式でgetとremoveを組み合わせることは可能ですか?

52
Marco Stramezzi

リストから要素を削除するには

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

89
uma shankar

スレッドはかなり古いですが、まだ解決策を提供すると考えられています-Java8を使用します。

removeIf関数を使用します。時間の複雑さはO(n)です

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

APIリファレンス: removeIf docs

仮定:producersProcedureActiveListです

注:このアプローチでは、削除されたアイテムを保持することはできません。

25
asifsid88

タスクを実行するためにVanilla Javaイテレータを使用することを検討してください。

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. stream()サポート(少なくともイテレーターでremove()を実装しているもの)がなくても、Iterableで実行できます。

短所

  1. 単一の式として所定の場所で実行することはできません(補助的な方法または変数が必要です)

については

ラムダ式でgetとremoveを組み合わせることは可能ですか?

他の答えは明らかにそれが可能であることを示していますが、あなたは知っておくべきです

  1. 検索と削除はリストを2回走査する場合があります
  2. 反復されるリストから要素を削除するときにConcurrentModificationExceptionがスローされる場合があります
15

直接的な解決策は、 ifPresent(consumer) によって返されるOptionalで findFirst() を呼び出すことです。オプションが空でない場合、このコンシューマが呼び出されます。また、現在のコードのように、検索操作で空のオプションが返された場合でも例外がスローされないという利点もあります。代わりに、何も起こりません。

削除した値を返したい場合は、mapを呼び出して、Optionalremoveの呼び出し結果に戻すことができます。

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) 操作がインデックスに対して直接動作します。

9
Tunaki

古いリストを変更したくない場合は、Java 8のフィルターを使用して別のリストを作成できます。

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

これは人気のない答えになると確信していますが、うまくいきます...

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

p[0]は、見つかった要素を保持するか、nullになります。

ここでの「トリック」は、実質的に最終的なarray参照を使用することで「実質的に最終的な」問題を回避することですが、最初の要素を設定します。

3
Bohemian

Eclipse Collections を使用すると、任意のJava.util.Listでremove(int)とともにdetectIndexを使用できます。

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

Eclipseコレクションの MutableList タイプを使用する場合、リストで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
Donald Raab

他の人が示唆しているように、これはループとイテラブルのユースケースかもしれません。私の意見では、これが最も簡単なアプローチです。リストをインプレースで変更したい場合は、とにかく「実際の」関数型プログラミングとは見なせません。ただし、条件を満たした要素を含む新しいリストと満たさない要素の新しいリストを取得するには、Collectors.partitioningBy()を使用できます。もちろん、このアプローチでは、条件を満たす複数の要素がある場合、最初の要素だけでなく、それらすべてがそのリストに含まれます。

1
user140547

タスク:get is and✶リストから要素を削除

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

以下のロジックは、元のリストを変更しないソリューションです

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
KimchiMan

私の最初のアイデアとあなたの答えを組み合わせて、私は自分の質問の解決策と思われるものに到達しました:

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が示唆するとおり)and削除されたオブジェクトを関数呼び出し元に返します。

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; 
            });
}
0
Marco Stramezzi

リストから複数の要素を新しいリストに追加し(述語を使用してフィルター処理)、既存のリストから削除するしたい場合、どこでも適切な答え。

Java St​​reaming 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セクション。

0