前言
在刷LeetCode时发现这个问题,主要是当时一个Integer
list使用流处理后转成数组,由于精度问题,需要将再转成long进行计算,出现上述无法强转问题。
原因
1、list的toArray()无参方法返回的实际是原来list中元素的类型数组,如果需要强制转化的话要考虑原来元素类型能不能转成需要转成的类型。
2、list的toArray(intArr)带数组参数方法,若传入的数组长度小于list的元素大小,则会创建一个新的数组,赋值list中的元素到新数组中,返回数组的引用,否则直接将元素赋值到传入数组中。
主要是由于list与 array相互转化而产生的问题。
public void testCast() {
HashSet<Integer> integers = new HashSet<>();
integers.add(1);
integers.add(2);
List<Integer> collect = integers.stream().sorted().collect(Collectors.toList());
// first test
Object[] ints = collect.toArray();
for (Object i : ints) {
System.out.println((long) i); // 反编译后,基本变量long变为Long类型。
}
// second test
Integer[] intArr = new Integer[]{};
collect.toArray(intArr);
int length = intArr.length;
System.out.println(length);
}
类似含义的代码参考上方。
分析
使用toArray()无参方法转成Object[]数组
第一种尝试中,虽然表面结果是Object类型的数组,实则各个变量都是原来list中变量类型,所以这里的数组其实是Integer类型的,而实体类型Integer与Long之间是无法强制转化的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2BRK8TlN-1663577038779)(https://secure2.wostatic.cn/static/GahpnTX3QtZwVjSE11gWj/image.png)]
看一下list的toArray()方法。
由于List接口中的toArray()
规定返回为Object[],所以实现该接口方法返回的都是Object[],但是实际实现中可能是Object子类。
在代码中使用的是ArrayList,下面看一下ArrayList的toArray()
实现。
ArrayList.java
/**
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this list. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this list in
* proper sequence
*/
public Object[] toArray() {
// 直接调用帮助类Arrays.copyOf()方法,将list中的
return Arrays.copyOf(elementData, size);
}
Arrays.java
@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
在Arrays中传入了原来的class类型,并且新建数组时判断是否为Object类型,如果过是Object类型,创建的是Object数组,否则根据传入的类型创建数组。
所以直接使用list的toArray()方法实际上是返回原有的List中的元素类型数组。
使用toArray(T[] a)转成数组
第二种尝试中,虽然表面结果是Object类型的数组,实则各个变量都是原来list中变量类型,所以这里的数组其实是Integer类型的,而实体类型Integer与Long之间是无法强制转化的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wh3ZGbBA-1663577038780)(https://secure2.wostatic.cn/static/fFskncjcWy5n9cVNLhXRyu/image.png)]
ArrayList.java
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
如果传入的数组长度小于list元素集合大小,则会使用Arrays.copyOf创建一个新的数组,否则的话直接将list中的元素赋值到传入的数组中,大于元素长度的数组赋值为null。同样这里的数组元素和原来list集合中的元素是一样的。