3

If my API provides a stream, is there a way for clients to modify the underlying collection?

For example:

class ListWrapper {
  List<String> myList;
  ...
  Stream<String> getStream() {
    return myList.stream();
  }
}

Is it possible for a client to call listWrapper.getStream() and somehow modify the contents of myList?

Josh Grinberg
  • 523
  • 6
  • 14
  • Modify the structure of `myList` or modify the individual elements within it? – Savior May 25 '20 at 14:47
  • Either. I'm wondering if clients can change myList in any way (e.g. change the order of the elements, or change the contents of any given element). – Josh Grinberg May 25 '20 at 14:48
  • see https://stackoverflow.com/questions/30778017/modifying-objects-within-stream-in-java8-while-iterating – Savior May 25 '20 at 14:49
  • 1
    This question is slightly different. I believe that question assumes the client has access to the underlying source collection (e.g. `myList`). This question is asking about the mutability risks of exposing a stream to clients. – Josh Grinberg May 25 '20 at 15:00

1 Answers1

9

A stream cannot modify the underlying collection (if the collection itself is not exposed), so elements cannot be added or removed, nor can the order be changed. When you consume a stream in a terminal operation, a new collection is built (but you can collect into another existing collection).

However, what is still possible is for stream operations to modify single elements if the elements themselves are mutable. This is bad practice, but it can be done.

Example:

class Item { public int x; public Item(int x) { this.x = x; } }
var list = List.of(new Item(1), new Item(2));
list.stream().peek(item -> item.x = 3).collect(toList());

When the stream is consumed with collect (or count or forEach or any other terminal operation), the peek callback will modify each element. After running above code, your list will contain 2 items with a value of x=3. peek is an arbitrarily chosen non-terminal operation. The same behavior can be observed with filter, map/flatMap, or any other non-terminal operation.

In spirit this is very similar to: Is Java "pass-by-reference" or "pass-by-value"?, but minimally better hidden.

knittl
  • 246,190
  • 53
  • 318
  • 364