在Java中无法创建一个LinkedList数组...?

103
我正在开发一个稀疏矩阵的类,它需要使用一个LinkedList数组来存储矩阵的值。数组中的每个元素(即每个LinkedList)代表矩阵的一行。而LinkedList数组中的每个元素则代表一列和其对应的值。
在我的类中,我声明了这个数组:
private LinkedList<IntegerNode>[] myMatrix;

我在SparseMatrix的构造函数中尝试定义:

myMatrix = new LinkedList<IntegerNode>[numRows];

我最终得到的错误是

无法创建一个泛型数组 LinkedList<IntegerNode>

所以,我有两个问题:

  1. 我做错了什么?
  2. 如果无法创建数组,为什么在声明数组时类型是可以接受的?

IntegerNode 是我创建的一个类。而且,所有的类文件都被打包在一起。

9个回答

145

由于某些原因,您需要进行类型转换并像这样进行声明:

myMatrix = (LinkedList<IntegerNode>[]) new LinkedList<?>[numRows];

我研究了一个类似的问题,并阅读了上面的转换是集合框架中广泛使用的常见“hack”。 - luke
17
我认为这应该是被选中的答案。我没有做过实验,但是我有一种直觉,Sergey的第二种方法会产生相当大的开销; 我确定第一种方法会。在多个方面,列表不如数组高效,虽然我不会在这里详细说明,但是我已经做过实验,发现使用列表时速度明显变慢。与添加到列表中相比,更快的方法是自己管理数组并重新分配它们。 - Ricket
4
我仍然会得到一个“Type safety: unchecked cast”警告。对我来说,Bob的解决方案看起来最为干净。 - Marco Lackovic
3
在JDK 7中,上述代码会产生一个rawtypes警告。可以使用未限定的<?>类型来解决这个问题,但是仍然会收到一个未经检查的警告(可以被抑制)。例如:<br><code>myMatrix = (LinkedList<IntegerNode>[]) new LinkedList<?>[numRows];</code> - Neon
这是最好的解决方案。我认为这是Java中的一个缺陷。 - sudo
显示剩余3条评论

66

你不能使用通用数组创建。这是java泛型的一个缺陷/特性。

不带警告的方法有:

  1. 使用列表的列表而不是列表的数组:

List< List<IntegerNode>> nodeLists = new LinkedList< List< IntegerNode >>();
  • 声明用于列表数组的特殊类:

  • class IntegerNodeList {
        private final List< IntegerNode > nodes;
    }
    

    19
    更好的替代方案是:class IntegerNodeList extends List<IntegerNode> {}该代码段表示创建一个名为IntegerNodeList的类,它扩展自List<IntegerNode> - kamasheto
    这个实现非常慢。获取[1000][2000]元素(nodeLists.get(1000).get(2000))将使LinkedList迭代3000次!如果有人可能会对其进行索引,请避免使用LinkedList。ArrayList将更快地进行索引,但Fredrik的解决方案总体上更好。 - Steve Zobell

    5
    除了语法问题之外,我认为使用数组和链表来表示矩阵很奇怪。要能够访问矩阵的任意单元格,您可能需要一个实际的数组或至少一个ArrayList来保存行,因为LinkedList必须从第一个元素遍历整个列表到任何特定元素,这是一个O(n)操作,而ArrayList或实际数组则快得多,只需O(1)。
    虽然您提到这个矩阵是稀疏的,但也许更好的存储数据的方法是将其作为映射的映射存储,其中第一个映射中的键表示行索引,其值是一个行映射,其键是列索引,其值是您的IntegerNode类。因此:
    private Map<Integer, Map<Integer, IntegerNode>> myMatrix = new HashMap<Integer, Map<Integer, IntegerNode>>();
    
    // access a matrix cell:
    int rowIdx = 100;
    int colIdx = 30;
    Map<Integer, IntegerNode> row = myMatrix.get(rowIdx); // if null, create and add to matrix
    IntegerNode node = row.get(colIdx); // possibly null
    

    如果您需要能够按行遍历矩阵,则可以将行映射类型设置为TreeMap,对于按索引顺序遍历列也是同样的,但如果您不需要这些情况,则HashMapTreeMap更快。当然,有用的帮助方法来获取和设置任意单元格,并处理未设置的空值,会非常有用。

    4
    class IntegerNodeList extends LinkedList<IntegerNode> {}
    
    IntegerNodeList[] myMatrix = new IntegerNodeList[numRows]; 
    

    你忘了为LinkedList添加泛型。 - Peter Wippermann

    3

    myMatrix = (LinkedList<IntegerNode>[]) new LinkedList[numRows];

    这样的强制类型转换虽然可以工作,但仍会留下一个讨厌的警告:

    "类型安全性:类型List[]的表达式需要未经检查的转换。"

    声明一个用于列表数组的特殊类:

    class IntegerNodeList { private final List< IntegerNode > nodes; }

    是避免警告的巧妙想法。也许更好的方法是使用接口:

    public interface IntegerNodeList extends List<IntegerNode> {}
    

    那么

    List<IntegerNode>[] myMatrix = new IntegerNodeList[numRows];
    

    编译时没有警告。

    看起来还不错,是吧?


    IntegerNodeList:你会在哪个类中使用它?例如,你不能将 ArrayList <IntegerNode> 分配给它。你需要扩展 ArrayList 以实现这一点... - Hans-Peter Störr
    不需要在数组初始化之外使用IntegerNodeList接口:List<IntegerNode>[] myMatrix = new IntegerNodeList[5];for (int i = 0; i < myMatrix.length; i++) {myMatrix[i] = new ArrayList();} - user306708
    1
    List<IntegerNode>[] myMatrix = new IntegerNodeList[numRows]; 这段代码存在微妙但重要的问题。你只能在数组中放置 IntegerNodeListmyMatrix[i] = new ArrayList<IntegerNode>(); 这行代码会抛出 ArrayStoreException 异常。 - Radiodef

    2

    2
    List<String>[] lst = new List[2];
    lst[0] = new LinkedList<String>();
    lst[1] = new LinkedList<String>();
    

    没有任何警告。NetBeans 6.9.1,jdk1.6.0_24


    1
    在没有警告的情况下,但使用Oracle的Java SE 6 Update 32编译时,我遇到了编译错误:“类型List不是泛型;它不能用参数<String>进行参数化”。删除<String>参数会生成另一个错误:“类型不匹配:无法将LinkedList<String>转换为List”。 - Marco Lackovic

    0
    你需要一个列表数组,其中一种选择是尝试:
    private IntegerNode[] node_array = new IntegerNode[sizeOfYourChoice];
    

    然后node_array[i]存储一个ArrayList<IntegerNode>LinkedList<IntegerNode>(任何你喜欢的列表实现)的头(第一个)节点。

    在这种设计下,您失去了随机访问方法list.get(index),但是您仍然可以从类型安全数组中存储的头/第一个节点开始遍历列表。

    这可能取决于您的用例而成为可接受的设计选择。例如,我使用此设计来表示图的邻接表,在大多数情况下,它需要遍历邻接表以获取给定顶点,而不是随机访问列表中的某个顶点。


    0
    如果我按照以下方式操作,就会出现所提到的错误信息。
    LinkedList<Node>[] matrix = new LinkedList<Node>[5];
    

    但是如果我在声明中只删除列表类型,似乎就会具有所需的功能。

    LinkedList<Node>[] matrix = new LinkedList[5];
    

    这两个声明在我不知道的方式上有很大的不同吗?
    编辑
    啊,我现在想我遇到了这个问题。
    在for循环中迭代矩阵并初始化列表似乎可以工作。虽然它不像其他提供的解决方案那样理想。
    for(int i=0; i < matrix.length; i++){
    
        matrix[i] = new LinkedList<>();
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接