列表数组和变通方法

时间:2014-07-08 09:39:06

标签: java arrays list arraylist

我想创建一个ArrayLists数组,类似于此线程中的数组:How to do an array of hashmaps?。但是,Java会发出警告

  

“无法创建ArrayList的通用数组< String>”

当我尝试执行以下操作时     ArrayList [] arrayOfLists = new ArrayList [size];

我有点理解所提供的问题和解决方法。 我有自己的方法,遗憾的是也无法解决问题。

我尝试创建一个ArrayLists列表,然后使用toArray()。

ArrayList<ArrayList<String>> listOfLists = new ArrayList<ArrayList<String>>();  
ArrayList<String>[] arrayOfLists = (ArrayList<String>[])listOfLists.toArray();

工作得很好,但得到了警告:

  

类型安全:从Object []到ArrayList&lt; String&gt; []

的未选中强制转换

当我尝试检查类型安全时,使用

if(listOfLists.toArray() instanceof ArrayList<String>[]) 

我收到错误:

  

无法对参数化类型ArrayList&lt; String&gt; []执行instanceof检查。使用表单ArrayList&lt;?&gt; []代替,因为在运行时将删除其他泛型类型信息

为什么我不能使用这种方法?为什么toArray()返回Object[]而不是ArrayList<String>,因为实例是使用ArrayList<String>;类型初始化的?
关于如何完成这项工作的任何其他解决方法/建议? 2D数组不起作用,因为不同的列表的大小可能会有很大差异。

2 个答案:

答案 0 :(得分:2)

当前接受的答案在描述Java的泛型时存在重大错误,所以我觉得我应该回答以确保没有任何误解。

Java中的泛型是完全编译时特性,并且由于擦除而在运行时大部分都不存在(在某些情况下,您可以使运行时咳出泛型类型信息,但这与一般情况相差甚远)。这为您的问题答案提供了基础。

  

为什么我不能使用这种方法?

由于泛型被删除,运行时ArrayList<String>[](以及所有其他参数化ArrayList<>[]实例)实际上是ArrayList[]。因此,运行时无法检查某些内容是否为instanceof ArrayList<String>[],因为运行时实际上并不知道String是您的类型参数 - 它只是看到ArrayList[]

  

为什么toArray()返回Object []而不是ArrayList,因为实例是用theArrayList初始化的;类型?

再次,擦除。类型参数将被删除为Object,因此在运行时您实际拥有的是ArrayList<Object>。由于这种擦除,运行时没有返回正确类型数组所需的信息;它只知道ArrayList持有Object,因此返回Object[]。这就是toArray(T[])重载存在的原因 - 数组保留其类型信息,因此可以使用数组提供必要的类型信息以返回正确类型的数组。

  

关于如何完成这项工作的任何其他解决方法/建议?

正如您所看到的,混合泛型和数组并不能很好地工作,所以理想情况下,您不会将List和数组混合在一起。因此,如果可能,您应该使用List<List<String>>或类似的内容而不是List<String>[]。但是,如果您想保留ArrayList<String>[]可以执行此操作:

@SuppressWarnings("unchecked")
ArrayList<String>[] array = new ArrayList[size];

您仍然可以获得未经检查的类型警告,但您可以合理地确定不会遇到堆污染,因为对象的唯一引用是array。您也可以将其用作toArray()的参数:

@SuppressWarnings("unchecked")
ArrayList<String>[] temp = new ArrayList[0];
ArrayList<String>[] arrayOfLists = listOfLists.toArray(temp);

@SuppressWarnings("unchecked")
ArrayList<String>[] arrayOfLists = listOfLists.toArray((ArrayList<String>[]) new ArrayList[0]);

有关无法参数化数组的原因的更多信息,请参阅this SO question。简而言之,这样的事情是不安全的,因为数组是协变的,而泛型是不变的。

答案 1 :(得分:0)

问题是Generics是在运行时创建的,但类型转换和数组大小必须在编译时检查。编译器无法告诉编译时期内的ArrayList<String>类(因为它将在稍后生成),它只能告诉它至少是一个Object,因为Java中的每个类至少是一个对象。你可以进行类型转换并禁止警告甚至可以工作,但是你会陷入陷阱,意外地将类型混淆在一起并弄乱你的代码。

Java是一种类型安全的语言,可以防止您在日常工作中遇到程序员最常出现的错误之一:混淆变量类型。因此,虽然可以进行类型转换,但作为即将到来的优秀Java程序员,您不应该这样做。如果需要这样的构造,请使用ArrayList<ArrayList<String>>,并仅在必要时使用数组。

使用数组的主要原因是执行速度,因为显然使用对象会使运行时忙于一些开销。不使用数组的主要原因是这种开销会让您在编码时更加灵活,并减少您所犯的错误。因此,作为一般建议:除非您知道(如测量并确定为瓶颈)您需要速度,请使用列表。 Java甚至可以进行一些内部优化,超出了您希望将列表加速到非常接近阵列执行速度的程度。