Di aliran Java8, apakah saya diizinkan untuk mengubah / memperbarui objek di dalamnya? Misalnya. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Di aliran Java8, apakah saya diizinkan untuk mengubah / memperbarui objek di dalamnya? Misalnya. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Jawaban:
Ya, Anda dapat mengubah status objek di dalam aliran Anda, tetapi paling sering Anda harus menghindari modifikasi status sumber aliran. Dari bagian non-interferensi dari dokumentasi paket streaming kita dapat membaca bahwa:
Untuk sebagian besar sumber data, mencegah interferensi berarti memastikan bahwa sumber data tidak diubah sama sekali selama eksekusi pipeline streaming. Pengecualian penting untuk ini adalah aliran yang sumbernya adalah koleksi serentak, yang dirancang khusus untuk menangani modifikasi serentak. Sumber aliran bersamaan adalah mereka yang
Spliterator
melaporkanCONCURRENT
karakteristiknya.
Jadi ini OK
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
tetapi ini dalam banyak kasus tidak
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
dan mungkin melempar ConcurrentModificationException
atau bahkan pengecualian tak terduga lainnya seperti 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
yang mencegah mengubah daftar (menghapus / menambahkan elemen baru) saat melakukan iterasi dan kedua aliran dan untuk-setiap loop menggunakannya. Anda dapat mencoba menggunakan loop sederhana seperti for(int i=0; ..; ..)
yang tidak memiliki masalah ini (tetapi tidak akan menghentikan Anda ketika utas lain akan mengubah daftar Anda). Anda juga bisa menggunakan metode seperti list.removeAll(Collection)
, list.removeIf(Predicate)
. Juga java.util.Collections
kelas memiliki beberapa metode yang dapat berguna seperti addAll(CollectionOfNewElements,list)
.
filter()
Cara fungsionalnya adalah:
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]
}
}
Dalam solusi ini daftar asli tidak diubah, tetapi harus berisi hasil yang Anda harapkan dalam daftar baru yang dapat diakses di bawah variabel yang sama dengan yang lama
Untuk melakukan modifikasi struktural pada sumber aliran, seperti yang disebutkan Pshemo dalam jawabannya, salah satu solusinya adalah membuat instance baru dari Collection
sejenis ArrayList
dengan item di dalam daftar utama Anda; mengulangi daftar baru, dan melakukan operasi pada daftar utama.
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
Untuk menyingkirkan ConcurrentModificationException Gunakan CopyOnWriteArrayList
List<Something>
- Anda tidak tahu apakah itu CopyOnWriteArrayList. Jadi ini lebih seperti komentar yang biasa-biasa saja, tapi bukan jawaban yang sebenarnya.
Alih-alih membuat hal-hal aneh, Anda bisa filter()
dan kemudian map()
hasilnya.
Ini jauh lebih mudah dibaca dan pasti. Aliran akan membuatnya hanya dalam satu putaran.
Anda dapat menggunakan removeIf
untuk menghapus data dari daftar secara bersyarat.
Misalnya: - Jika Anda ingin menghapus semua nomor genap dari daftar, Anda dapat melakukannya sebagai berikut.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
Ya, Anda juga dapat mengubah atau memperbarui nilai objek dalam daftar dalam kasus Anda:
users.stream().forEach(u -> u.setProperty("some_value"))
Namun, pernyataan di atas akan membuat pembaruan pada objek sumber. Yang mungkin tidak dapat diterima dalam banyak kasus.
Untungnya, kami memiliki cara lain seperti:
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
Yang mengembalikan daftar yang diperbarui kembali, tanpa menghambat yang lama.
Seperti yang disebutkan sebelumnya - Anda tidak dapat mengubah daftar asli, tetapi Anda dapat mengalirkan, mengubah, dan mengumpulkan item ke dalam daftar baru. Berikut adalah contoh sederhana bagaimana memodifikasi elemen string.
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]
}
}
Ini mungkin sedikit terlambat. Tapi ini salah satu penggunaannya. Ini untuk mendapatkan hitungan jumlah file.
Buat pointer ke memori (object baru dalam kasus ini) dan modifikasi properti objek. Aliran Java 8 tidak mengizinkan untuk memodifikasi penunjuk itu sendiri dan karenanya jika Anda mendeklarasikan hanya menghitung sebagai variabel dan mencoba menambah dalam aliran itu tidak akan pernah berfungsi dan memunculkan pengecualian kompiler di tempat pertama
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 + "]";
}
}