In Java8 streams, am I allowed to modify/update objects within?
For eg. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
In Java8 streams, am I allowed to modify/update objects within?
For eg. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Yes, you can modify or update the values of objects in the list in your case likewise:
users.stream().forEach(u -> u.setProperty("some_value"))
However, the above statement will make updates on the source objects. Which may not be acceptable in most cases.
Luckily, we do have another way like:
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
Which returns an updated list back, without hampering the old one.
You can make use of the removeIf
to remove data from a list conditionally.
Eg:- If you want to remove all even numbers from a list, you can do it as follows.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
This might be a little late. But here is one of the usage. This to get the count of the number of files.
Create a pointer to memory (a new obj in this case) and have the property of the object modified. Java 8 stream doesn't allow to modify the pointer itself and hence if you declare just count as a variable and try to increment within the stream it will never work and throw a compiler exception in the first place
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 + "]";
}
}
To do structural modification on the source of the stream, as Pshemo mentioned in his answer, one solution is to create a new instance of a Collection
like ArrayList
with the items inside your primary list; iterate over the new list, and do the operations on the primary list.
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
The functional way would imho be:
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]
}
}
In this solution the original list is not modified, but should contain your expected result in a new list that is accessible under the same variable as the old one
Instead of creating strange things, you can just filter()
and then map()
your result.
This is much more readable and sure. Streams will make it in only one loop.
As it was mentioned before - you can't modify original list, but you can stream, modify and collect items into new list. Here is simple example how to modify string element.
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]
}
}
Yes, you can modify state of objects inside your stream, but most often you should avoid modifying state of source of stream. From non-interference section of stream package documentation we can read that:
So this is OK
but this in most cases is not
and may throw
ConcurrentModificationException
or even other unexpected exceptions like NPE: