【问题标题】:Can I determine the number of dimensions of a multi-dimensional list if it contains empty lists?如果多维列表包含空列表,我可以确定它的维数吗?
【发布时间】:2015-06-01 18:12:28
【问题描述】:

我有两个类AB 和一个方法B convertAToB(A a)。现在我想创建一个将A 的多维(甚至是单维)列表转换为B 的方法。例如:

  • 给定一个List<A>,它应该返回一个List<B>
  • 给定一个List<List<A>>,它应该返回一个List<List<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&lt;List&lt;List&lt;A&gt;&gt;&gt;,那么我们将返回{{{b1, b2}, {b3, b4}}, {}}。这里外部列表中的第二个元素 ({}) 在参数中具有 List&lt;List&lt;A&gt;&gt; 类型,但实际上在返回值中只有 List&lt;B&gt; 类型。你能安全地将一个空列表从List&lt;B&gt; 转换为List&lt;List&lt;B&gt;&gt; 吗?我不知道,但无论如何,这种“可变深度”场景可以通过在递归方法中添加一个参数来跟踪达到的最大深度并确保所有空列表向下钻取到相同的深度来解决。但是,即使在运行时知道有多少维,我也不知道如何创建多维列表。例如,在这种情况下,我知道在运行时有两个维度,我想创建一个新的 ArrayList&lt;ArrayList&lt;Object&gt;&gt; 而不是 ArrayList&lt;Object&gt;,但我不知道如何以编程方式进行。

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

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

总结:

  • 可以将空的List&lt;B&gt; 转换为List&lt;List&lt;B&gt;&gt; 吗?
  • 有没有办法找出多维空列表的维数?
  • 有没有办法在给定 n 的运行时返回一个空的 n 维列表(即最外面的列表为空)?
  • 调用者是否知道由于类型擦除,我返回了“错误类型”的对象?

【问题讨论】:

    标签: java list generics multidimensional-array casting


    【解决方案1】:

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

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

    可以将空的List&lt;B&gt; 转换为List&lt;List&lt;B&gt;&gt; 吗?

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

    有没有办法找出多维空列表的维数?

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

    有没有办法在给定 n 的运行时返回一个空的 n 维列表(即最外面的列表为空)?

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

    调用者是否知道由于类型擦除,我返回了“错误类型”的对象?

    只有当他们对列表做了一些意想不到的事情时,就像我上面提到的那样。如果他们传入List&lt;A&gt;,而您将其转换为List&lt;B&gt;,则他们很可能会将Bs 插入其中。从那时起,尝试与原始 List&lt;A&gt; 交互将引发混乱的 ClassCastExceptions,因为在预期仅包含 As 的列表中有 Bs。

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


    当您处理维度数未知的多维列表时,有什么方法可以“不”解决编译器的类型安全问题?

    如果不了解您 actually 试图做什么,很难回答您的问题,但从广义上讲,避免这种情况的方法是重新检查您的要求,因为它们是有问题的。您的数据具有任意深度这一事实很奇怪;您可以对其进行重组,使其不会任意深吗?或许将depth 编码为AB 中的实例变量,并简单地拥有ABLists?

    如果您真的需要任意深度的多维数据,显然List 不是表示它的正确方式。您实际上是在描述Tree,因此请考虑以这种方式表示它(包含一个值和节点列表的根节点)。您可能还喜欢 Guava 的Table

    【讨论】:

    • 当您处理维度数未知的多维列表时,有什么方法可以“不”解决编译器的类型安全问题? Java 中没有 List&lt;List&lt;...&lt;List&lt;A&gt;&gt;...&gt;&gt; 类型的类型(省略号是文字,类似于变量参数方法)。在实践中,我这样做的原因是我正在构建一个 API 方法,该方法将此类类型擦除为“Object”,但我不认为他们的 API 一定是错误的,原因与刚才所说的相同。
    • PS:当我提到转换时,我只是指调用者进行转换,而不是在我的方法内部进行转换。因为我要返回 List&lt;Object&gt;,他们可能会将其转换为 List&lt;List&lt;B&gt;&gt;,认为应该是这样,但实际上我只返回了 List&lt;B&gt;
    • @Kidburla 更新了我的答案以解决您的第一条评论。删除类型信息的 API 是一个糟糕的 API。关于您的第二条评论,如果您的方法没有删除类型信息,调用者就不必处理猜测。无论是您进行转换还是调用者进行转换,将 List 转换为不同的泛型类型都可能(并且可能会)引入复杂的错误。
    【解决方案2】:

    1) 是的,一个空列表(如果您确定它是空的,并且永远不会使用对该列表的旧交替类型引用)可以安全地转换为具有任何泛型类型的空列表,无论编译器警告,如下所示:

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

    2) 在运行时,不存在“多维空列表”之类的东西。

    3) 见 2.

    4) 否。new ArrayList&lt;A&gt;()new ArrayList&lt;ArrayList&lt;A&gt;&gt;()new ArrayList&lt;ArrayList&lt;ArrayList&lt;A&gt;&gt;&gt;() 将返回结构相同的对象。

    【讨论】:

      猜你喜欢
      • 2012-09-30
      • 1970-01-01
      • 2021-06-16
      • 2014-03-13
      • 2012-07-04
      • 2013-05-17
      • 1970-01-01
      • 2018-09-30
      • 1970-01-01
      相关资源
      最近更新 更多