【问题标题】:having trouble making a method that creates a 2d array out of a T type unrolled linked list在创建从 T 类型展开的链表中创建二维数组的方法时遇到问题
【发布时间】:2021-12-17 11:15:37
【问题描述】:
public T[][] getArrayOfBlocks() {
    Node node = this.first;
    @SuppressWarnings("unchecked")
    T[][] result = (T[][]) new Object[this.nNodes][this.arraySize];
    for(int i = 0; i < this.nNodes; i++)
    {
        for(int j = 0; j < this.arraySize; j++)
            if(node.a[j] != null)
                result[i][j] = node.a[j];
        node = node.next;
    }
    return result;
}

(我是java新手,所以我的措辞会有点奇怪) 我试图制作一种从 T 型展开链表中创建二维数组的方法。当我使用 Integer 类而不是 T 类型测试上面的方法时,我收到一个错误提示

线程“main”中的异常 java.lang.ClassCastException: class [[Ljava.lang.Object;不能转换为类 [[Ljava.lang.Integer; ([[Ljava.lang.Object; 和 [[Ljava.lang.Integer; 在加载器'bootstrap'的模块java.basa中)

所以是的,我想知道是否有任何方法可以在不更改返回类型的情况下解决此错误。 在此先感谢:)

【问题讨论】:

  • 你有什么理由需要投射它吗?您应该可以改用:new T[this.nNodes][this.arraySize];
  • @LogonMacdougall new T[] 不是有效的 java。

标签: java generics arraylist multidimensional-array


【解决方案1】:

y 在 T 型展开链表中。

这是不可能的。

泛型是编译器想象出来的。一旦您的 Java 代码被转换为类文件,或者如果它们没有(签名中的泛型),泛型完全消失,JVM 会将其视为注释。它完全没有效果。相反,编译器使用它来生成错误或警告并注入不可见的强制转换。这个:

List<String> x = new ArrayList<String>();
x.add("Hello");
String y = x.get(0);

在类代码中与编译没有区别:

List x = new ArrayList();
x.add("Hello");
String y = (String) x.get(0);

如果您难以理解这个想法,请尝试一下。编写两者,编译它,运行javap -c -v 来查看字节码。完全相同。

x.add(5) 不能替代x.add("Hello") 的原因仅仅是因为javac 不会让它发生。如果你破解 javac 以允许它,你会得到一个类文件就好了,它验证就好了。 x.add(5) 甚至会执行得很好。你会在下一行得到一个 ClassCastException,仅仅是因为你将 Integer 的一个实例强制转换为 String。

因此,无法分辨运行时new ArrayList&lt;String&gt;();new ArrayList&lt;Integer&gt;() 之间的区别。明显地;泛型消失了;这两个都只是new ArrayList(),就是这样。

相比之下,数组是“具体化”的:它们不是 javac 想象的虚构。你实际上可以在运行时得到这些东西。 new String[0]new Integer[0]是有区别的:

Object[] arr1 = new String[0];
System.out.println(arr1.getClass().getComponentType()); // prints 'String'

不可能为泛型编写相同的代码:

List<?> list1 = new ArrayList<String>();
System.out.println(--nothing you can write here will print String--);

因此,在您的“使用 T 展开的代码”中,T 不是您可以转换为实际运行时类型的东西,这意味着不可能创建一个 T 数组。

仍然很难相信这一点?细读java.util.List 的API,特别是它包含的各种toArray 方法。

查看无参数的:toArray()。这里有两种解释:

  • 这个类的设计者是个彻头彻尾的白痴,因为它返回Object[],这很愚蠢,因为显然应该返回T[]
  • 或者,也许正在发生其他事情,他们“知道”实际上不可能在那里返回 T[]

正如本文的其余部分希望已经提出的那样,这是第二个原因。

幸运的是,还有另外 2 个 toArray 方法,这两个 do 都返回 T[],如您所愿。它们都基于 调用者 努力为您提供 T 类型的概念。

第一个版本是toArray(T[] in)。如果 toArray 代码足够大,它将使用提供的数组,但如果不是,它只会创建一个大小合适的新数组并返回它。在实践中,您总是调用 listOfStrings.toArray(new String[0])(您可能认为 new String[list.size()] 会更快 - 不,那会更慢1。一个很好的例子说明为什么编写更复杂的代码,因为它看起来更快是不好的想法。JVM 过于复杂,无法像这样预测性能)。

这里的技巧是,列表的 toArray 中的代码将获取该数组,获取其类(将创建的数组抛在一边),从中获取组件类型,然后使用它来创建一个新数组。

还有一个:toArray(IntFunction&lt;T[]&gt; arrayCreator)(您需要查看javadoc of Collection 才能看到它;它是继承的)。

这里我们要求调用者提供创建新数组的代码。你可以这样使用它:listOfStrings.toArray(String[]::new)

选择你的毒药,或者两者都加。任何一种技巧都可以在这里发挥作用:

public T[][] getArrayOfBlocks(T[] dummy) {
  Class<?> componentType = dummy.getClass().getComponentType();

  @SuppressWarnings("unchecked")
  T[][] arr = (T[][]) java.lang.reflect.Array.newInstance(componentType, this.nNodes, this.arraySize);

 .. code continues here ..
}

或:

public T[][] getArrayOfBlocks(BiFunction<Integer, Integer, T[][]> arrayMaker) {
  T[][] arr = arrayMaker.apply(this.nNodes, this.arraySize);

 .. code continues here ..
}

Yes, they are both annoying. There are other options but they have significant downsides - the above 2 options are your best bet. That or forget about arrays. Why do you even want a `T[][]` in the first place? Arrays can't grow or shrink, assuming it's not a primitive array (and this isn't, by definition; generics cannot be primitive) they are not more performant, and their toString/equals/hashCode implementations are surprising (that's programmer-ese for 'basically broken'). Their API is non-existent. Why would you want to offer it?


1) In case you desire explanations for this one: It's because the toArray code is hotspot intrinsiced and knows it doesn't need to wipe out the memory space, whereas with `new String[100]`, those 100 references all need to be nulled out first because java guarantees you can't 'see' uninitialized memory.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-06
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 2022-01-05
    • 1970-01-01
    相关资源
    最近更新 更多