二维数组的列表

3

我需要一个二维列表矩阵,例如ArrayList,并且想知道在这种情况下什么是最好的选择。它只需要是4x4大小。

我应该使用像

    ArrayList[][] foo = new ArrayList[4][4];

或者

    ArrayList<SomeClass>[][] foo = new ArrayList[4][4];

在 for 循环中初始化每个元素的适当类型或

    ArrayList<ArrayList<ArrayList<SomeClass>>> foo = ArrayList<ArrayList<ArrayList<SomeClass>>>();

第一种方法会产生警告,提示需要进行参数化。如果我使用第二种方法,则会收到未经检查的转换警告。但是,如果循环遍历元素并在那里初始化它们,即使仍然收到警告,也不应该有任何问题,对吗?最后一种方法不会产生任何警告,并且可能运行良好,但感觉有点凌乱。
编辑:尽管我的问题有点不太清楚,但我得到了一些不错的答案。基本上是如何制作列表的表格。创建一个自定义类来处理行/列可以使其变得更容易。

第二种方法应该是这样的:ArrayList<SomeClass>[][] foo = new ArrayList<SomeClass>[4][4];,但是拥有一个列表数组似乎有些尴尬。你真的需要那个二维数组吗?如果是这样,请提供一些更多的信息。 - Thomas
使用ArrayList<SomeClass>[][] foo = new ArrayList<SomeClass>[4][4]; 会产生错误 "无法创建ArrayList<SomeClass>的泛型数组",这就是为什么我将其省略的原因 :) - Mattias
我需要它的原因是我有一个包含“结果”的表。根据操作的结果,我想将这些对象添加到2D数组中的其中一个列表中。这样做是否使事情更清晰? :) - Mattias
4个回答

6

将第一种方法修改如下:

List[][] foo = new ArrayList[4][4];

第二种方法不是你所需要的。你试图创建一个四维数组,而不是一个具有4 * 4元素的二维数组。

此外,我想给你提个建议:永远不要在赋值语句左边使用具体类,即 ArrayList list = .... 使用 List list = ...

避免使用过于复杂的数据结构。二维数组列表太过复杂。创建一个封装了某些功能的自定义类,然后创建该类对象的集合/数组(最好是一维数组)。


好的,谢谢你的提示!你能解释一下为什么在赋值语句的左边永远不要使用具体类吗?对于自定义类,我仍然希望得到类似于二维数组的东西。但是可以将其实现为一维数组,并创建一些方法,例如 getRow()getColumn() 吗? - Mattias
在大多数情况下,您应该使用接口而不是具体类,因为这样如果需要,您可以将ArrayList替换为LinkedList。如果您需要更多关于功能(如排序或非排序)的信息,则可能有其他接口(请考虑SetSortedSet),但是了解具体实现对方法的用户来说很少有用。 - Thomas
@AlexR 啊,我明白了。谢谢你的解释!我想我现在已经弄清楚了,我创建了一个自定义类来定义每一列,并编写了一个方法将结果插入到正确的位置。这让一切变得更容易了。 - Mattias
嗨,使用泛型可以吗? - Ace McCloud

1

以下是具有显式初始化的 2x2 矩阵示例。

List<MyClass>[][] matr = new List<MyClass>[][] {
    new List<MyClass> { new ArrayList<MyClass>(), new ArrayList<MyClass>() },
    new List<MyClass> { new ArrayList<MyClass>(), new ArrayList<MyClass>() }
}

0

就像Alex已经说过的那样,拥有一个二维列表数组非常复杂,容易出错或被错误使用。

由于您的数组应该表示一个表格,您可能希望为行或列创建类,具体取决于哪个更重要。

或者,如果您确切地知道要使用多少个单元格,您可以为表中的每个单元格创建一个类并管理一个二维单元格数组:

class Cell {
  private List<SomeClass> content = new ArrayList<SomeClass>();
  ...
}

Cell[][] matrix = new Cell[4][4];  //don't forget to initialize each cell

0
    public static class Grid<T> {

    public interface TFactory<T> {

        T create();
    }
    private T[] data;
    private int d1;
    private int d2;

    public Grid(int d1,
            int d2,
            Class<T> clazz,
            TFactory<T> fac) {
        this.d1 = d1;
        this.d2 = d2;
        data = (T[]) Array.newInstance(clazz, d1 * d2);
        for (int i = 0; i < data.length; i++) {
            data[i] = fac.create();
        }
    }

    public T get(int c1,
            int c2) {
        return data[c1 * d2 + c2];
    }
}

public static class ArrayListFactory<X> implements Grid.TFactory<ArrayList<X>> {

    public ArrayList<X> create() {
        return new ArrayList();
    }
}

public static void main(String[] args) throws RTXException, ParseException {
    // cannot use new new ArrayList<String>[4][4];, because of generic array creation error
    List<String>[][] a2d = new ArrayList[4][4];
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            a2d[i][j] = new ArrayList<String>();
        }
    }
    Grid g = new Grid(4, 4, ArrayList.class, new ArrayListFactory<String>());
}

这里有两种方法。第一种是你所要求的,第二种是封装版本(将2D数组转换为1D数组以便于内部操作)。

正如之前所说,尽可能将变量声明为接口(但不要过多)。 这意味着,如果你想表达的仅仅是一个列表,并且不希望任何人根据具体的实现方式来执行不同的行为,则只需使用List而不是ArrayList作为变量类型。 但是如果你使用LinkedSet,那么使用LinkedSet作为变量类型(以及函数的返回值),可以承诺它快速列出所有元素并按插入顺序进行排序。如果你使用Set,使用者不应该依赖于任何特定的迭代顺序。

顺便提醒一下,要小心显式初始化。它是一种不错的语法,但它会创建匿名内部类,并且可能导致父类的意外保留。但是,如果你知道自己在做什么,而且没有外部引用,那就可以放心使用。


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