Java8ストリームでは、オブジェクトを変更/更新できますか?例えば。List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Java8ストリームでは、オブジェクトを変更/更新できますか?例えば。List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
回答:
はい、ストリーム内のオブジェクトの状態を変更できますが、ほとんどの場合、ストリームのソースの状態を変更することは避けてください。ストリームパッケージドキュメントの非干渉セクションから、次のように読むことができます。
ほとんどのデータソースでは、干渉を防ぐということは、ストリームパイプラインの実行中にデータソースがまったく変更されないようにすることを意味します。これに対する注目すべき例外は、同時変更を処理するように特別に設計された同時コレクションをソースとするストリームです。並行ストリームソースは、その特性を
Spliterator
報告するソースCONCURRENT
です。
だからこれは大丈夫です
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
しかし、これはほとんどの場合そうではありません
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
そして、ConcurrentModificationException
NPEのような他の予期しない例外をスローする可能性があります。
List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());
list.stream()
.filter(i -> i > 5)
.forEach(i -> list.remove(i)); //throws NullPointerException
Iterator
、反復中にリストの変更(新しい要素の削除/追加)が妨げられ、ストリームとfor-eachループの両方がそれを使用することを避ける必要があります。for(int i=0; ..; ..)
この問題がないような単純なループを使用してみることができます(ただし、他のスレッドがリストを変更しても停止しません)。list.removeAll(Collection)
、などのメソッドを使用することもできますlist.removeIf(Predicate)
。また、java.util.Collections
クラスには、のように役立つ可能性のあるメソッドがいくつかありますaddAll(CollectionOfNewElements,list)
。
filter()
機能的な方法は次のようになります。
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTestRun {
public static void main(String[] args) {
List<String> lines = Arrays.asList("a", "b", "c");
System.out.println(lines); // [a, b, c]
Predicate<? super String> predicate = value -> "b".equals(value);
lines = lines.stream().filter(predicate.negate()).collect(toList());
System.out.println(lines); // [a, c]
}
}
このソリューションでは、元のリストは変更されませんが、古いリストと同じ変数でアクセスできる新しいリストに期待される結果が含まれている必要があります
ConcurrentModificationExceptionを取り除くには、CopyOnWriteArrayListを使用します
List<Something>
場合、それがCopyOnWriteArrayListであるかどうかはわかりません。つまり、これは平凡なコメントのようなものですが、本当の答えではありません。
はい、同様に、リスト内のオブジェクトの値を変更または更新できます。
users.stream().forEach(u -> u.setProperty("some_value"))
ただし、上記のステートメントはソースオブジェクトを更新します。ほとんどの場合、これは受け入れられない可能性があります。
幸いなことに、次のような別の方法があります。
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
これは、古いリストを妨げることなく、更新されたリストを返します。
前に述べたように、元のリストを変更することはできませんが、アイテムをストリーミング、変更、および新しいリストに収集することはできます。文字列要素を変更する簡単な例を次に示します。
public class StreamTest {
@Test
public void replaceInsideStream() {
List<String> list = Arrays.asList("test1", "test2_attr", "test3");
List<String> output = list.stream().map(value -> value.replace("_attr", "")).collect(Collectors.toList());
System.out.println("Output: " + output); // Output: [test1, test2, test3]
}
}
これは少し遅いかもしれません。しかし、ここに使用法の1つがあります。これは、ファイル数のカウントを取得します。
メモリへのポインタ(この場合は新しいobj)を作成し、オブジェクトのプロパティを変更します。Java 8ストリームではポインタ自体を変更できないため、カウントを変数として宣言し、ストリーム内でインクリメントしようとすると、動作せず、そもそもコンパイラ例外がスローされます。
Path path = Paths.get("/Users/XXXX/static/test.txt");
Count c = new Count();
c.setCount(0);
Files.lines(path).forEach(item -> {
c.setCount(c.getCount()+1);
System.out.println(item);});
System.out.println("line count,"+c);
public static class Count{
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Count [count=" + count + "]";
}
}