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