Valette_Renoux
Valette_Renoux

Reputation: 366

Java: offer a filtered view on a class

I have a class with a large amount of data, and I would like to expose a "filtered view" on this object with the same methods, but a modified output. To give a very basic example, let's say that I have such a class:

public class Booleans {
    private boolean[] data;

    public Booleans(boolean[] data) {
        this.data = data;
    }

    public boolean getDataAt(int i) {
        return data[i];
    }

    public Booleans opposite() {
        // What to return here? b.opposite().getDataAt(i) should be !b.getDataAt(i)
    }
}

Is there a good pattern to write the opposite method? I need to be as memory efficient as possible: the data can not be duplicated, and a call to "opposite" should ideally not create any object, as it will be called many times.

Creating one small object in the constructor of Booleans would be fine for example, but I can not refer "this" at that point...

Upvotes: 2

Views: 115

Answers (6)

fsegui
fsegui

Reputation: 31

If your concern is memory efficiency, why not use a BitSet instead of an array?

Each element in a boolean[] array takes 1 byte of memory while each boolean in a BitSet takes 1 bit of memory rounded to the next byte.

Also, i would just create a second oppositeBitSet, and whenever you modify the originalBitSet with value, just modify oppositeBitSet with !value.

And, the bitset has

void flip(int fromIndex, int toIndex) Sets each bit from the specified fromIndex (inclusive) to the specified toIndex (exclusive) to the complement of its current value.

**I just started studying collections, so let me know if something here is incorrect or why bitSets are not advisable.

Upvotes: 0

Kedar Mhaswade
Kedar Mhaswade

Reputation: 4695

If I understand correctly, you want to provide a way to keep the interface of your class the same, but the implementation should be different (in this case opposite) as far as some methods are concerned. It is not clear what kind of constraints you have, other than the apparent fact that you don't want to be creating too many objects.

I am suggesting a solution that may be initially frowned upon, but may actually help in case you have many such methods some of which should just pass through, others not so. This is called the dynamic proxy pattern. Its implementation is somewhat slower than the more straightforward approach, but it may fit your needs. I fear that the complexity is slightly more than what you may want, but I thought it is better to have options from which you can choose.

I suggest that you extract the interface out first:

interface BooleanArray {
    /** Returns the boolean at given index */
    boolean getDataAt(int i);
    /** Returns a BooleanArray implementation that is "opposite" */
    BooleanArray opposite();
}

Then provide one concrete implementation: Booleans. Now, for the opposite() method, we are going to piggyback on the existing target implementation and then complement the results.

Below is complete (working) code that demonstrates the idea.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** Proxy-based attempt to: http://stackoverflow.com/questions/35805928/java-offer-a-filtered-view-on-a-class
 */
interface BooleanArray {
    boolean getDataAt(int i);
    BooleanArray opposite();
}
public class Booleans implements BooleanArray {
    private final boolean[] data;
    private volatile BooleanArray OPPOSITE;

    public Booleans (boolean[] data) {
        this.data = data;
    }

    private Object initOpposite(BooleanArray target) {
        return Proxy.newProxyInstance(Booleans.class.getClassLoader(),
                new Class[]{BooleanArray.class},
                new ComplementHandler(target));
    }

    public boolean getDataAt(int i) {
        return data[i];
    }

    public BooleanArray opposite() {
        if (OPPOSITE == null)
            OPPOSITE = (BooleanArray) initOpposite(this);
        return OPPOSITE;
    }

    public static void main(String[] args) {
        BooleanArray ab = new Booleans(new boolean[]{true, false, true});
        BooleanArray ba = ab.opposite();
        for (int i = 0; i < 3; i ++)
            System.out.println(ab.getDataAt(i) + " and: " + ba.getDataAt(i));
    }
    private static final class ComplementHandler implements InvocationHandler {
        private final BooleanArray target;
        public ComplementHandler(BooleanArray target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("getDataAt".equals(method.getName())) {
                Boolean orig = (Boolean) method.invoke(target, args);
                if (orig)
                    return Boolean.FALSE;
                return Boolean.TRUE;
            }
            return method.invoke(target, args);
        }
    }
}

Upvotes: 1

Durandal
Durandal

Reputation: 20059

You cannot get away with no object creation. You can however get away with a very cheap object creation:

private transient Booleans opposite;

static class BooleansOpposite extends Booleans {
     Booleans original;

     BooleansOpposite(Booleans original) {
         super(null);
         this.original = original;
     }

     public Booleans opposite() {
         return original;
     }

     public boolean getDataAt(int i) {
          return !original.getDataAt(i);
     }
}

public Booleans opposite() {
    if (opposite == null) {
        opposite = new BooleansOpposite(this);
    }
    return opposite;
}

This basically uses the Decorator pattern to alter the behavior of the getDataAt method. Although the first call of opposite creates an object, the only cost you pay is the one for BooleansOpposite that holds no data, since it refers back to its parent. You could also create the opposite instance ahead of time in the constructor if you prefer eager initialization.

It would work even better if Booleans were just an interface or a pure abstract class that does not define any members, then the BooleansOpposite implementation would not need to inherit useless fields.

Upvotes: 1

Mshnik
Mshnik

Reputation: 7032

You can't do it without creating a single object (you do have to return something that isn't this), but you can do it without recreating data. (Hence O(1) space).

One solution is to create a new instance that doesn't store any data (thus no need to copy the data), but have it always return the opposite of this' data.

  public Booleans opposite() {
    return new Booleans(){
      @Override
      public boolean getDataAt(int i) {
        return !vals[i];
      }
    };
  }

Upvotes: 0

That method should return a new Object Booleans but** all the elements of that array are just the negation or complement of the actual instance.

example:

Upvotes: 0

djechlin
djechlin

Reputation: 60768

Probably return an Iterator and program the iterator to skip the value you're concerned about. O(# of skipped items) memory which I think is optimal.

Upvotes: 0

Related Questions