如果包含空列表,我可以确定多维列表的维数吗?

Can I determine the number of dimensions of a multi-dimensional list if it contains empty lists?

我有两个 类 AB 以及一个方法 B convertAToB(A a)。现在我想制作一种方法,将 A 的多维(甚至一维)列表转换为 B。例如:

等等。我的转换方法不需要由编译器进行完全类型检查,原因很明显...该方法将自行进行类型检查。

在嵌套层次结构中的任何位置都没有空列表的情况下,这非常简单:

public static List<Object> convertAListToBList(List<Object> aList) {
    List<Object> bList = new ArrayList<Object>();

    for (Object obj : aList) {
        if (obj instanceof A) {
            bList.add(convertAToB((A)obj));
        } else if (obj instanceof List) {
            bList.add(convertAListToBList((List<Object>)obj));
        } else {
            throw new UnsupportedOperationException("Unknown object in hierarchy");
        }
    }
    return bList;
}

但是,如果有空列表,情况就会变得有点复杂。例如,如果我们传入一个包含 {{{a1, a2}, {a3, a4}}, {}}List<List<List<A>>>,那么我们将返回 {{{b1, b2}, {b3, b4}}, {}}。这里外部列表中的第二个元素 ({}) 在参数中具有类型 List<List<A>> 但实际上在 return 值中只有类型 List<B> 。你能安全地将空列表从 List<B> 转换为 List<List<B>> 吗?我不知道,但无论如何,这种 "variable-depth" 场景可以通过向递归方法添加一个参数来跟踪达到的最大深度并确保所有空列表向下钻取到相同的深度来解决。但是,我不知道如何创建多维列表,即使在运行时确实知道有多少维。例如,在这种情况下,我知道在运行时有两个维度,我想创建一个新的 ArrayList<ArrayList<Object>> 而不是 ArrayList<Object> 但我不知道如何以编程方式执行此操作。

在从未达到最大深度的情况下,它变得更加复杂。例如,如果我传入一个仅包含 {}List<List<A>> 类型的对象,那么我将返回一个仅包含 {}List<B> 类型的对象。我不知道是否有任何方法可以知道原始列表中有多少维度并创建适当的类型。

我是不是太担心了,因为所有这些东西都会在运行时被删除?

总结:

Am I worrying too much because all this stuff will be erased at runtime anyway?

从某种意义上说,是的 - 您发布的方法应该同样适用于空列表和填充列表。这并不是说它会工作很好,但它不会比无空列表情况有更多问题。

Can an empty List<B> be cast to List<List<B>>?

一个空的 List 总是可以转换为另一个通用类型,因为它实际上不包含被转换类型的任何值。 但是这是一个不好的做法,因为您现在很可能有两个对您的列表的引用,它们认为它包含不同的类型,这将导致 ClassCastExceptions 如果您尝试添加它的元素。 不可变List,例如ImmutableList 总是安全的。

Is there anyway to find out the number of dimensions of a multi-dimensional empty list?

这真的没有意义;空列表没有维度。由于未编译泛型类型,因此在运行时无法知道它可能有多少维度。

Is there any way to return an empty n-dimensional list (i.e. outermost list empty) at runtime given n?

不,但是再次这样做在运行时没有意义。

the caller ever have any idea that I returned objects of the "wrong type", due to type erasure?

只有当他们对列表做了意想不到的事情时,就像我上面提到的那样。如果他们传入一个 List<A> 并且你将它和 return 转换为 List<B>,他们很可能会在其中插入 B。从那时起,尝试与原始 List<A> 进行交互将引发令人困惑的 ClassCastException,因为列表中有 B,而预期仅包含 A

简而言之,这种模式很危险。考虑重新审视您的要求或您的实施。事实上,您正在解决编译器的类型安全问题,这只会导致问题。


Is there any way to "not" work around the compiler's type-safety when you are dealing with multi-dimensional lists with an unknown number of dimensions?

如果不了解您 actually 想要做什么,就很难回答您的问题,但从广义上讲,避免这种情况的方法是重新检查您的要求,因为它们是有问题的。您的数据具有任意深度这一事实很奇怪;你能重组它,让它不那么深吗?也许将 depth 编码为 AB 中的实例变量,并简单地具有 AB?

Lists

如果您确实需要任意深度的多维数据,显然 List 不是表示该数据的正确方法。您实际上是在描述 Tree, so consider representing it that way (a root node containing a value and a list of nodes). You might also like Guava's Table.

1) 是的,一个空列表(如果您确定它是空的,并且永远不会使用对该列表的旧替代类型引用)可以安全地转换为任何泛型的空列表,不管编译器警告如何,像这样:

ArrayList<String> l1 = new ArrayList<>();
ArrayList<ArrayList<String>> l2 = (ArrayList) l1;

2) 在运行时,没有"multi-dimensional empty list".

这样的东西

3) 见 2.

4) 否。new ArrayList<A>()new ArrayList<ArrayList<A>>()new ArrayList<ArrayList<ArrayList<A>>>() 将 return 结构相同的对象。