我想创建一个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数组不起作用,因为不同的列表的大小可能会有很大差异。
答案 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甚至可以进行一些内部优化,超出了您希望将列表加速到非常接近阵列执行速度的程度。