Ward
Ward

Reputation: 2828

Java modifiable view on collection

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

  1. Start situation:

    Collection A = {1, 2, 3};  
    View-on-collection B = {1, 2};
    
  2. Method call:

    someMethod(B) {  
        B.add(4);  
        B.remove(2);  
    }
    
  3. End situation:

    Collection A = {1, 3, 4};
    

Does anyone know a neat solution to this problem?

Upvotes: 6

Views: 992

Answers (4)

eboix
eboix

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

Keppil
Keppil

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

Adrian Regan
Adrian Regan

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

AlexR
AlexR

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

Related Questions