Java:提供类的过滤视图

Java:提供类的过滤视图,java,oop,object,Java,Oop,Object,我有一个包含大量数据的类,我想用相同的方法在这个对象上公开一个“过滤视图”,但要修改输出。 举一个非常基本的例子,假设我有这样一个类: public class Booleans { private boolean[] data; public Booleans(boolean[] data) { this.data = data; } public boolean getDataAt(int i) { return data[

我有一个包含大量数据的类,我想用相同的方法在这个对象上公开一个“过滤视图”,但要修改输出。 举一个非常基本的例子,假设我有这样一个类:

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)
    }
}
是否有一个好的模式来编写相反的方法?我需要尽可能地提高内存效率:数据不能被复制,对“反向”的调用在理想情况下不应该创建任何对象,因为它将被多次调用


例如,在布尔的构造函数中创建一个小对象就可以了,但我不能在那一点上引用“this”…

可能返回一个
迭代器,并对迭代器进行编程以跳过您关心的值。O(#个跳过的项目)内存,我认为这是最佳的。

该方法应该返回一个新的布尔对象,但**该数组的所有元素只是实际实例的否定或补充


示例:

如果不创建单个对象,则无法执行此操作(您必须返回不是此
的内容),但可以执行此操作,而无需重新创建数据。(因此为O(1)空间)

一种解决方案是创建一个不存储任何数据的新实例(因此无需复制数据),但它总是返回与此“数据”相反的结果

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

如果不创建对象,您将无法逃脱。但是,您可以使用非常便宜的对象创建:

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;
}
这基本上是使用Decorator模式来改变getDataAt方法的行为。尽管第一次调用contract创建了一个对象,但您要支付的唯一成本是不包含数据的BooleansOpposite,因为它引用回其父对象。如果您更喜欢即时初始化,还可以在构造函数中提前创建相反的实例


如果Boolean只是一个接口或一个不定义任何成员的纯抽象类,那么它的工作效果会更好,那么BooleanSPosite实现就不需要继承无用的字段。

如果我理解正确,您希望提供一种方法来保持类的接口不变,但就某些方法而言,实现应该是不同的(在本例中是相反的)。除了不想创建太多对象这一明显事实之外,还不清楚您有什么样的约束

我建议的解决方案一开始可能会遭到反对,但如果你有很多这样的方法,其中一些应该通过,其他的则不会。这称为动态代理模式。它的实现比更直接的方法慢一些,但它可能适合您的需要。我担心它的复杂性会比你想要的稍高一些,但我认为最好有你可以选择的选项

我建议您首先提取接口:

interface BooleanArray {
    /** Returns the boolean at given index */
    boolean getDataAt(int i);
    /** Returns a BooleanArray implementation that is "opposite" */
    BooleanArray opposite();
}
然后提供一个具体的实现:
Booleans
。现在,对于
contract()
方法,我们将借助现有的目标实现,然后补充结果

下面是演示此想法的完整(工作)代码

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);
        }
    }
}
import java.lang.reflect.InvocationHandler;
导入java.lang.reflect.Method;
导入java.lang.reflect.Proxy;
/**基于代理的尝试:http://stackoverflow.com/questions/35805928/java-offer-a-filtered-view-on-a-class
*/
接口布尔数组{
布尔getDataAt(inti);
布尔数组反();
}
公共类布尔实现布尔数组{
私有最终布尔[]数据;
私有可变布尔数组;
公共布尔(布尔[]数据){
这个数据=数据;
}
私有对象(布尔数组目标){
返回Proxy.newProxyInstance(Booleans.class.getClassLoader(),
新类[]{BooleanArray.Class},
新成员(目标);
}
公共布尔getDataAt(int i){
返回数据[i];
}
公共布尔数组(){
如果(相反==null)
相反=(布尔数组)init相反(this);
反向返回;
}
公共静态void main(字符串[]args){
布尔数组ab=新布尔值(新布尔值[]{true,false,true});
布尔数组ba=ab.相反();
对于(int i=0;i<3;i++)
System.out.println(ab.getDataAt(i)+”和“+ba.getDataAt(i));
}
私有静态最终类补足处理程序实现调用处理程序{
私有最终布尔数组目标;
公共处理器(布尔数组目标){
this.target=目标;
}
@凌驾
公共对象调用(对象代理、方法、对象[]args)抛出Throwable{
if(“getDataAt”.equals(method.getName())){
Boolean orig=(Boolean)方法.invoke(target,args);
如果(原)
返回Boolean.FALSE;
返回Boolean.TRUE;
}
return method.invoke(target,args);
}
}
}

如果您关心的是内存效率,为什么不使用位集而不是数组

布尔[]数组中的每个元素占用1字节内存,而位集中的每个布尔元素占用1位内存,四舍五入到下一个字节

另外,我只需创建第二个对立位集,并且每当您使用值修改originalBitSet时,只需使用值修改对立位集!价值观

而且,位集具有

无效翻转(从索引到索引的整数,从索引到索引的整数) 将从指定的fromIndex(包含)到指定的toIndex(独占)的每一位设置为其当前值的补码


**我刚开始研究集合,所以请告诉我这里是否有不正确的地方,或者为什么不建议使用位集。

您错过了示例:)如果
contract
无法创建任何对象,这意味着它必须改变当前实例,是吗?如中所示,
otherit()
必须返回
this
?我不能使用otherit