要素のリストが与えられたら、特定のプロパティを持つ要素を取得しますandリストから削除します。私が見つけた最良の解決策は次のとおりです。
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
ラムダ式でgetとremoveを組み合わせることは可能ですか?
リストから要素を削除するには
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
スレッドはかなり古いですが、まだ解決策を提供すると考えられています-Java8
を使用します。
removeIf
関数を使用します。時間の複雑さはO(n)
です
producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));
APIリファレンス: removeIf docs
仮定:producersProcedureActive
はList
です
注:このアプローチでは、削除されたアイテムを保持することはできません。
タスクを実行するために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;
}
利点:
stream()
サポート(少なくともイテレーターでremove()
を実装しているもの)がなくても、Iterable
で実行できます。短所:
については
ラムダ式でgetとremoveを組み合わせることは可能ですか?
他の答えは明らかにそれが可能であることを示していますが、あなたは知っておくべきです
ConcurrentModificationException
がスローされる場合があります直接的な解決策は、 ifPresent(consumer)
によって返されるOptionalで findFirst()
を呼び出すことです。オプションが空でない場合、このコンシューマが呼び出されます。また、現在のコードのように、検索操作で空のオプションが返された場合でも例外がスローされないという利点もあります。代わりに、何も起こりません。
削除した値を返したい場合は、map
を呼び出して、Optional
をremove
の呼び出し結果に戻すことができます。
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]
は、見つかった要素を保持するか、nullになります。
ここでの「トリック」は、実質的に最終的なarray参照を使用することで「実質的に最終的な」問題を回避することですが、最初の要素を設定します。
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コレクションのコミッターです
他の人が示唆しているように、これはループとイテラブルのユースケースかもしれません。私の意見では、これが最も簡単なアプローチです。リストをインプレースで変更したい場合は、とにかく「実際の」関数型プログラミングとは見なせません。ただし、条件を満たした要素を含む新しいリストと満たさない要素の新しいリストを取得するには、Collectors.partitioningBy()
を使用できます。もちろん、このアプローチでは、条件を満たす複数の要素がある場合、最初の要素だけでなく、それらすべてがそのリストに含まれます。
タスク: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
} ) );
以下のロジックは、元のリストを変更しないソリューションです
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;
}
リストを再度たどらない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;
});
}
リストから複数の要素を新しいリストに追加し(述語を使用してフィルター処理)、既存のリストから削除するとしたい場合、どこでも適切な答え。
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セクション。