为什么Java中不能有"List<List<String>>"类型的数据结构?

30
在Java中,为什么下面这行代码不起作用?
List<List<String>> myList = new ArrayList<ArrayList<String>>();

如果我将其更改为以下内容,则可以正常工作

List<ArrayList<String>> myList = new ArrayList<ArrayList<String>>();

起初,我认为可能不能使用接口创建列表,但是我可以很好地创建List<Runnable>

有什么想法吗?


5
List<List<String>> myList = new ArrayList<List<String>>(); 这个怎么样? - madth3
@madth3 如果List是java.awt.List,而不是与ArraList<?>相同的包,则new ArrayList<List<String>>()才能正常工作。 - cl-r
List是java.awt中的一个类,也是java.util包中的一个接口。因此,您必须注意哪个对您有好处。由于使用了ArrayList,似乎您正在使用java.util,并且java.util.List可能会列在您的导入中。 : 1°) 如果您想要一个ArrayListArrayList,那么new ArrayList<ArrayList<String>>()很好。2°)new ArrayList<List<String>>()将编译为java.awt.List<String> - cl-r
6个回答

46

通用类型更加严谨。

List 表示 List 或其子类型,但是 <List> 只表示 List。如果你想要一个子类型,需要使用 <? extends List>

我猜你可以使用

List<List<String>> myList = new ArrayList<List<String>>();
你不能这样做的原因是可能会使用引用的引用,再加上额外的间接层次需要小心处理。
// with one level of indirection its simple.
ArrayList alist = new ArrayList();
List list = aList; // all good
list = new LinkedList(); // alist is still good.

通过使用泛型,您可以拥有两个间接级别,这可能会导致问题,因此更注重避免这些问题。

// with two levels of indirection
List<ArrayList> alist = new ArrayList<ArrayList>();
List<List> list = (List) alist; // gives you a warning.
list.add(new LinkedList()); // adding a LinkedList into a list of ArrayList!!
System.out.println(alist.get(0)); // runtime error

打印

Exception in thread "main" java.lang.ClassCastException: java.util.LinkedList
     cannot be cast to java.util.ArrayList

1
是的,看起来可以工作。你能再解释一下为什么吗?谢谢。 - Nosrettap
1
你可以将 LinkedList<String> 添加到 List<List<String>> 中。但是你不能将 LinkedList<String> 添加到 ArrayList<ArrayList<String>> 中。因此,你不能创建一个类型为 List<List<String>> 的变量来引用一个 ArrayList<ArrayList<String>> ,因为它们具有不同的行为。 - Louis Wasserman
对于非泛型类型,您可以将 ArrayList 分配给 List,因为它会隐式地为您转换。对于泛型类型,您不能将 <ArrayList> 强制转换为 <List>,因为它们是不同的类型。您可以将 <ArrayList> 强制转换为 <? extends List>,因为 ArrayList 扩展了 List。 - Peter Lawrey
1
java.util.List是一个接口(java.awt.List是一个类),它不能被实例化。ArrayList是实现了java.util.List<E>接口的具体实例,建议在字段中将其替换为接口,就像您在第一个示例中所做的一样。 - cl-r
问题的根源在于可以更改列表,添加非符合类的对象。如果不更改列表,则问题会消失。有没有一种声明不更改列表的方法? - fishinear
一个 List<?> 可以支持 get(n)remove(e),但不支持 add(e),这可以防止添加无效的引用。(除非你进行强制类型转换) - Peter Lawrey

10

让我们从这个开始:

    ArrayList<ArrayList<String>> myList = new ArrayList<ArrayList<String>>();

这段代码创建了一个元素为ArrayList的ArrayList。

现在假设我们可以将它赋值给

    List<List<String>> myList2 = myList.

现在,我们应该能够做到这一点:

    myList2.add(new LinkedList<String>());

但是这意味着我们向一个元素应该是ArrayLists的列表中添加了一个LinkedList。糟糕!!!

实际上,将myList赋值给myList2是不合法的...这确保了不可能向ArrayList<ArrayList<String>>对象中添加错误类型的List<String>。(不,Peter,这不仅仅是纠结小节 :-) )


3

只有顶级集合可以被声明为实现类,而嵌套的集合必须保留接口,直到您实际创建实例:

List<List<String>> rootList = new ArrayList<List<String>>(); 

然后当您创建一个要放入的元素时,将其作为实现:

List<String> nodeList = new ArrayList<String>();
rootList.add(nodeList);

“……嵌套的接口必须保持为接口,直到您实际创建实例”这并不是正确的。List<ArrayList<String>> x 是一个有效的声明,然后 x = new ArrayList<ArrayList<String>>(); 也可以工作。 - ars-longa-vita-brevis

1

这是一篇与IT相关的文章,内容如下:

它比较了从左侧(声明)到右侧(实例化)的Type。在左侧,您的类型是List<String>,而在右侧,它是ArrayList<String>。如果存在差异,请进行更改。

请将右侧(实例化)更新为List。

   List<List<String>> myList = new ArrayList<List<String>>();

这应该可以正常工作。


0

我知道这是一个老问题,但我想分享我的想法。

与其创建一个列表的列表,我个人更喜欢创建一个类型数组的列表 (List<Type[]> listArray = new ArrayList<Type[]>();),然后生成一个仅包含类型的单独列表 (List<Type> list = new ArrayList<Type>();),最后使用 .add(list.toArray())。这样,它比混乱的列表语法更清晰、更易读。

例如,在最近的一个项目中,我有一个输入文件,其中每行只有一个“0”,表示原始文件中的新行(这是一个加密算法):

String[] input = getInputContents(inFile);
List<String> currentBuffer = new ArrayList<String>();
List<String[]> buffers = new ArrayList<String[]>();

for(String line : input) {
    if(line.equals("0")) {
        buffers.add((String[])currentBuffer.toArray());
        currentBuffer = new ArrayList<String>();
    } else {
        currentBuffer.add(line);
    }
}

0

如果列表中包含一个以上的列表,则允许使用list<list<string>> l1=new list<list<string>>();

public final class CPanelXMLBuilder extends PanelXMLBuilder {

    public CPanelXMLBuilder(AuthenticatedUser pAuthenticatedUser, Map<String, Object> pSessionMap, Map<String, Object> pRequestMap, String pPanelTemplate) throws Exception {
        super(pAuthenticatedUser, pSessionMap, pRequestMap, pPanelTemplate, null);
    }

    public Map<String, Object> buildXMLDocument(List<List<String>> pDetailsList) {

        if (pDetailsList.size() == 1) {
            List<String> pCustomerDetail = pDetailsList.get(0);
            xmlDocument.getRootElement().getChild("E_SHOW1").setText(pCustomerDetail.get(0));
            xmlDocument.getRootElement().getChild("E_SHOW2").setText(pCustomerDetail.get(1));
            xmlDocument.getRootElement().getChild("E_SHOW3").setText(pCustomerDetail.get(2));
            xmlDocument.getRootElement().getChild("E_SHOW4").setText(pCustomerDetail.get(3));
            xmlDocument.getRootElement().getChild("E_SHOW5").setText(pCustomerDetail.get(4));
            xmlDocument.getRootElement().getChild("ServerTimestamp").setText(pCustomerDetail.get(5).substring(0, 19));
        } else {
            xmlDocument.getRootElement().getChild("AlertType").setText("INFO");
            xmlDocument.getRootElement().getChild("Alert").setText("There is no matching record.");
        }

        requestMap.put(RequestMapKeys.XML_DOCUMENT, xmlDocument);

        return requestMap;
    }

}

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