Reputation: 5736
is there a filtered view of a collection (a subset of it) in Java, so that adding new item to it can also affect the source collection?
e.g.
List<String> source = new ArrayList<>();
source.add("a");
source.add("b");
List<String> view = source.stream().filter(i -> "b".equals(i)).collect(...);
view.add("c"); // source now contains also "c"
Upvotes: 4
Views: 236
Reputation: 305
Of course. You can create one by yourself by subclassing AbstractCollection
(which implements most of the methods in Collection
and you just need to write a few methods according to your needs). Then add the underlying collection as a member, override getters to apply the predicate to it and return the filtered results, override add(E)
to put elements, and then it can basically work.
In fact, Guava library has already provided Collections2.filter(Collection, Predicate)
(and also its set and map versions, but no list variant as indices become a problem when filtered) through which you can create a performant filtered view of any Collection
by a predicate function easily, allowing additions if the underlying collection is mutable and the predicate is satisfied. (Its mechanism is not complex, so you can also copy its implementation if not willing to include one more library ;) Please note that, however, as "c"
does not satisfy the predicate above, IllegalArgumentException
will be thrown in OP's example. But still, you can subclass AbstractCollection
and pass everything else to the filtered view but add to the underlying collection directly. The only problem is this kind of "add" operation will not follow collection's general contract.
Update: However, there seems to be no way to construct a two-way view from Stream
like OP's example, as operators do not know the sources of streams, and streams flow in one way.
Upvotes: 3
Reputation: 19545
JDK provides only index-based view of the list with the method List::subList
:
The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
Thus, the changes to the view can be applied to the source list too if the "filter" is constructed using List::indexOf
and/or List::lastIndexOf
.
This should be enough for a limited case of having a single range matching the condition:
List<String> source = new ArrayList<>();
source.add("a");
source.add("b");
source.add("b");
List<String> view = source.subList(source.indexOf("b"), source.lastIndexOf("b") + 1);
System.out.println("view: " + view);
view.add("c");
System.out.println("added view: " + view);
System.out.println("fixed src: " + source);
Output:
view: [b, b]
added view: [b, b, c]
fixed src: [a, b, b, c]
As for the filtered view of a list which stores all the source's elements matching some condition, such view would have to maintain a collection of sublists/subranges in general case and implement List
interface appropriately.
The Guava Collections2.filter
mentioned by @Wilderness Ranger above may be an example of the filtered view, however, its documentation states that its methods add
/addAll
would throw IllegalArgumentException
if the predicate is not satisfied, so in your example an attempt to add "c" would fail for the predicate "b"::equals
.
Upvotes: 2