Modifying Objects within stream in Java8 while iterating

JavaJava 8Java Stream

Java Problem Overview


In Java8 streams, am I allowed to modify/update objects within? For eg. List<User> users:

users.stream().forEach(u -> u.setProperty("value"))

Java Solutions


Solution 1 - Java

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:

>For most data sources, preventing interference means ensuring that the data source is not modified at all during the execution of the stream pipeline. The notable exception to this are streams whose sources are concurrent collections, which are specifically designed to handle concurrent modification. Concurrent stream sources are those whose Spliterator reports the CONCURRENT characteristic.

So this is OK

  List<User> users = getUsers();
  users.stream().forEach(u -> u.setProperty(value));
//                       ^    ^^^^^^^^^^^^^
//                        \__/

but this in most cases is not

  users.stream().forEach(u -> users.remove(u));
//^^^^^                       ^^^^^^^^^^^^
//     \_____________________/

and may throw ConcurrentModificationException or even other unexpected exceptions like 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

Solution 2 - Java

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

Solution 3 - Java

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

Solution 4 - Java

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

Solution 5 - Java

To get rid from ConcurrentModificationException Use CopyOnWriteArrayList

Solution 6 - Java

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.

Solution 7 - Java

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]
    }
}

Solution 8 - Java

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 + "]";
		}
		
		
		
	}

Solution 9 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTejas GokhaleView Question on Stackoverflow
Solution 1 - JavaPshemoView Answer on Stackoverflow
Solution 2 - JavaAndreas M. OberheimView Answer on Stackoverflow
Solution 3 - JavaMNZView Answer on Stackoverflow
Solution 4 - JavaJobinView Answer on Stackoverflow
Solution 5 - JavaKrupali_wadekarView Answer on Stackoverflow
Solution 6 - JavaNicolas ZozolView Answer on Stackoverflow
Solution 7 - JavaVadimView Answer on Stackoverflow
Solution 8 - Javavijayakumarpsg587View Answer on Stackoverflow
Solution 9 - JavaGaurav KhanzodeView Answer on Stackoverflow