【问题标题】:Java multidimensional hash/matrixJava 多维散列/矩阵
【发布时间】:2011-10-25 20:22:52
【问题描述】:

我有 n 个类,它们要么堆叠在一起,要么不堆叠在一起。所有这些类都扩展同一个类(CellObject)。我知道会有更多的类被添加到这个列表中,并且我想创建某种方式,以便在一个地方轻松操作“可堆叠性”。

我正在考虑创建一个矩阵,其中行索引是堆栈底部的类,列索引是堆栈顶部的类。如果可以上下堆叠,则该值为 true(或 1),否则为 false (0)。

但是,我的同事建议创建称为 canStack 的 n+1 个方法。一种通用的 canStack 方法将打开一个 instanceof 语句,该语句将其引导至 n 个子方法之一。每个子方法只回答顶部对象是否可以自己堆叠在底部对象上的问题。

我认为我的解决方案更优雅/干净。这是真的?如果是这样,我将如何实现它?

我将对象更改为类

【问题讨论】:

  • 也许在你的前几句话中将“对象”一词改为“类”。 Object 意味着一个实例,但这是关于类属性的。

标签: java multidimensional-array hashtable


【解决方案1】:

您的解决方案会更短。但是它有一个缺点,如果你添加一个 CellObject 的子类,你可能会忘记改变你的数组。即使您知道这应该发生,其他人也可能有一天会在代码上工作。再说一次,他的解决方案也有同样的问题。

现在,这是一个有点疯狂的想法,但是由于您本质上是在说 关于 类的东西,所以感觉就像元数据设施是有序的。您可以做的是定义一个注释,说明哪些类可以堆叠到带注释的类上和/或它可以堆叠在哪些类上。

类似这样的:

@interface Stackable {
    Class<? extends CellObject>[] stackables(); //Classes that may stack on the annotated one
    Class<? extends Cellobject>[] pillars(); //Classes this one can stack on
}

然后您可以创建一个使用此元数据的注释处理器。它可以创建一个您在编译时读入的配置文件,或者为您生成一些样板代码。您可以像 JPA 那样为其类型安全的查询 API 生成元类,以说明该类。或者您甚至可以在运行时保留注释以使用反射来找出可以堆叠在什么上的内容,构建您想要的临时数组,而不必对其进行编码。

如果您使用注释处理器,那么使用带有规范类名的字符串数组可能会更安全,因为 Class 对象在编译时可能还不可用。它的可行性还取决于所有 CellObject 类是否总是在同一个编译运行中。

使用反射(在确保注释具有 RetentionType RUNTIME 时可能)在这里似乎是一个可行的选择。检查数组;如果相应的元素为空(可以通过使用布尔值而不是布尔值来完成),则进行反射并填充该元素。下次你可以避免反射开销,根据需要懒惰地填充数组。

编辑:忘了提一下,我的解决方案也不会强制您保持元数据是最新的。此外,如果可堆叠性是可传递的,则可以降低复杂性。也就是说,A 可以堆叠在 B 上,B 可以堆叠在 C 上意味着 A 可以堆叠在 C 上。

【讨论】:

  • 啊,还有一件事。最好使用 (Hash)Map 来表示使用(限定)类名而不是 Class 对象的矩阵。将 Class 对象保存在集合中可能会导致严重的类加载器泄漏。
【解决方案2】:

矩阵方法将缩放为 O(n2)。相比之下,另一种方法的扩展时间为 O(n),但维护起来风险更大。

作为替代方案,考虑让abstract CellObject 实现合适的Stackable 接口,但将实现推迟到n 具体子类。编译器将立即识别缺失的实现。另见When an Abstract Class Implements an Interface

interface Stackable {

    boolean canStack(Stackable other);
}

abstract class CellObject implements Stackable {}

class Cell01 extends CellObject {

    @Override
    public boolean canStack(Stackable other) {
        return true; // TODO
    }
}

class Cell02 extends CellObject {

    @Override
    public boolean canStack(Stackable other) {
        return true; // TODO
    }
}
...

【讨论】:

  • 也考虑到这一点。问题是,这适用于实例而不是 Class 对象。也许海报确实需要类而不是实例的信息。但如果信息最终会与instanceofgetClass() 等一起使用,那么也可以使用您的方法。
  • 注解是新的 XML。把它们拍在所有东西上,就像洒在冰淇淋上一样。或者更好...从注释生成 XML!我就是这么做的。
【解决方案3】:

我认为您的矩阵概念不是实现目标的好方法。你最终会得到一个包含所有可能性的巨大矩阵。显然,从矩阵中提取您希望的信息将相当容易,但从长远来看,随着更多CellObject 子类的添加,维护它可能会被证明是一种痛苦的体验。这同样适用于您同事建议的 n + 1 种方法。

在这两种情况下,每次添加CellObject 的子类时,您都必须转到保存矩阵的类,为每个现有行创建一个新行和一个新列,并手动指定是否这个新类可以堆叠或不堆叠在类 x 上,或者为每个现有类添加一个新方法 canStackOnNewClassX ()。在我看来,这两种解决方案都容易出错(您可能很容易忘记更新矩阵,或者输入错误的信息,因为代码可能不容易阅读),有更优雅的方法来处理此类问题。

您可以做的一件事是在您的 CellObject 超类中拥有一个地图,该地图将保存您的“可堆叠性”信息,并提供填充此地图的方法以及检索 A 类的成员是否可以堆叠在B 类的成员。像这样:

public abstract class CellObject
    {
    private static Map<Class<? extends CellObject>, Map<Class<? extends CellObject>, Boolean>> fullStackabilityMap = 
            new HashMap<Class<? extends CellObject>, Map<Class<? extends CellObject>, Boolean>> ();

    protected static void addStackableOnObjectInformation (Class<? extends CellObject> baseObjectClass, Class<? extends CellObject> objectToStack, boolean canStackOnObject)
        {
        Map<Class<? extends CellObject>, Boolean> stackableMapForObject = fullStackabilityMap.get (baseObjectClass);

        if (stackableMapForObject == null)
            {
            stackableMapForObject = new HashMap<Class<? extends CellObject>, Boolean> ();
            fullStackabilityMap.put (baseObjectClass, stackableMapForObject);
            }

        stackableMapForObject.put (objectToStack, canStackOnObject);
        }

    protected boolean isStackableOnObject (CellObject baseObject)
        {
        Map<Class<? extends CellObject>, Boolean> stackableMapForObject = CellObject.fullStackabilityMap.get (baseObject.getClass ());

        if (stackableMapForObject == null)
            {
            return false;
            }

        Boolean canStackOnObject = stackableMapForObject.get (this.getClass ());
        return canStackOnObject != null ? canStackOnObject : false;  //Assume that the object cannot be stacked if it was not specified
        }
    }

public class CellObjectA extends CellObject
    {
    }

public class CellObjectB extends CellObject
    {
    static
        {
        addStackableOnObjectInformation (CellObjectB.class, CellObjectA.class, true);
        }
    }

public class CellObjectC extends CellObject
    {
    static 
        {
        addStackableOnObjectInformation (CellObjectC.class, CellObjectA.class, true);
        addStackableOnObjectInformation (CellObjectC.class, CellObjectB.class, true);
        }
    }

CellObject 中创建fullStackabilityMap 似乎很复杂,因为Java 在Java 6 中缺少菱形运算符,但是如果您编写一个创建地图的实用方法或使用Guava,则可以简化它。

因此,在此示例中,CellObjectC 实例不能堆叠在各种对象上; CellObjectB 实例只能堆叠在 CellObjectC 对象上,CellObjectA 可以堆叠在 CellObjectBCellObjectC 对象上。

每次添加新类时,您唯一要做的工作就是更新现有类的静态初始化程序,以确保考虑到这个新类。这种解决方案的优点是:

  • 您只需要指定哪种对象可以堆叠在哪种对象上。无需完全初始化具有所有可能性的矩阵。
  • 您可以直接询问对象是否可以堆叠在任何类型的对象上,而不必静态轮询外部类,这对我来说更易于维护,并且生成更简洁的代码。
  • 您不必维护 n+1 个方法,这些方法会告诉您对象 A 可以堆叠在对象 B 上,如果您最终得到大量 CellObject 子类,那将是一场彻头彻尾的噩梦。

【讨论】:

  • 很好的解决方案。我的建议或多或少归结为相同,但是静态初始化程序的工作被卸载到一些将检查注释的实用程序类。从代码的角度来看,您的方法更易于理解,而使用注释可能更直观。无论哪种方式,JavaDoc 的某些规范都必须非常清楚地表明 CellObject 类必须提供可堆叠性信息。
猜你喜欢
  • 2013-05-17
  • 2013-10-30
  • 2014-12-14
  • 1970-01-01
  • 1970-01-01
  • 2017-03-20
  • 1970-01-01
  • 2021-03-24
  • 1970-01-01
相关资源
最近更新 更多