Reputation: 2828
I'm looking to solve the following problem:
Starting with a collection A, I want to pass some kind of 'view' on that collection (say collection B) to a certain method. The view B does not necessary contain all the elements of the original collection A. If in this method objects are added to or removed from the view (collection B), these changes should also be reflected on the original collection A as well.
For instance (pseudo-code):
Start situation:
Collection A = {1, 2, 3};
View-on-collection B = {1, 2};
Method call:
someMethod(B) {
B.add(4);
B.remove(2);
}
End situation:
Collection A = {1, 3, 4};
Does anyone know a neat solution to this problem?
Upvotes: 6
Views: 992
Reputation: 5131
You could always have two different collections, a collection A and a collection B.
Then, whenever you added something to B
, you would add it to A
, and whenever removed something from B
you would also remove it from A
.
When removing from A
you would check whether or not B
contained the object to be removed, and, if so, you would remove it.
However, when adding to A
, you would not touch B
.
This might be less space efficient than an optimal solution, but it's not going to change time complexity (except for maybe removals from A
.)
Upvotes: -2
Reputation: 46239
One way is to use List.sublist()
:
public static void main(String[] args) {
List<Integer> aList = new ArrayList<Integer>(Arrays.asList(1,2,3));
List<Integer> view = aList.subList(0, 2);
view.add(new Integer(4));
view.remove(new Integer(2));
System.out.println("aList: " + aList);
System.out.println("view : " + view);
}
Another more general way would be through Guavas Collections2.filter()
, that lets you define a predicate to control which objects should be in the view:
public static void main(String[] args) {
List<Integer> aList = new ArrayList<Integer>(Arrays.asList(1,2,3));
@SuppressWarnings("unchecked")
Collection<Integer> view = Collections2.filter(aList, new Predicate() {
public boolean apply(Object arg0) {
return ((Integer) arg0).intValue() % 3 != 0;
}});
view.add(new Integer(4));
view.remove(new Integer(2));
System.out.println("aList: " + aList);
System.out.println("view : " + view);
}
Both examples print
aList: [1, 4, 3]
view : [1, 4]
Upvotes: 5
Reputation: 2250
You could extend AbstractList (or what ever abstract type of collection you are using)
In this abstraction, you can take the source collection in the constructor and hold a reference to it and the start and end points of the view of the original list
Override the add/remove/set methods so that those actions are also performed on the source collection also.
i.e.
class ListView<T> extends AbstractList<T> {
int start = 0;
int end = 0;
private Collection<T> original = null;
public ListView(List<T> original, int start, int end) {
this.original = original;
this.start = start;
this.end = end;
super.addAll(0, original.subList(start, end));
}
// Any add/set/remove must also alter the original
}
The ListView effectively should be a Proxy to the original list.
Alternatively, and with a bit more work, you could implement the Collection or List interface so that you work directly on the original list in a similiar fashion
You can then call your method or pass the ListView around as you would a normal collection.
i.e.
public void doSomeWork(Collection<String> collection);
...
object.doSomeWork(new ListView<String>(original, 0, 2));
Upvotes: 0
Reputation: 115378
Jacarta collections framework has such functionality. But this framework does not support generics. Take a look on Google Guava. I believe they should support such functionality too.
Upvotes: 0