利用Jdk 6260652 Bug解析Arrays.asList
在java.util.ArrayList源码中:
c.toArray might (incorrectly) not return Object[] (see 6260652) 产生疑惑:
附上Java Bug 网址:Java Bug Database
,可以根据关键词或bug id 查询详细信息
这个Bug的描述中可以看出:
原因:Arrays内部实现的ArrayList的toArray()方法的行为与规范不一致。
代码测试:
import java.util.*;
public class Test{
public static void demo1(){
System.out.println("this is demo1");
List<String> list=new ArrayList<>();
list.add("张三");
list.add("王五");
Object[] arr=list.toArray();
System.out.println(arr.getClass().getCanonicalName());
arr[0]=new Object();
Test.printArr(arr);
/*
正常编译、执行:
this is demo1
java.lang.Object[]
java.lang.Object@15db9742 王五
*/
}
public static void demo2(){
System.out.println("this is demo2");
List<String> list = Arrays.asList("张三", "王五");
Object[] arr=list.toArray();
System.out.println(arr.getClass().getCanonicalName());
arr[0]=new Object();
Test.printArr(arr);
/*
正常编译
执行输出:
this is demo2
java.lang.String[]
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Object
at Test.demo2(Test.java:31)
at Test.main(Test.java:55)
*/
}
public static void demo3() {
System.out.println("this is demo3");
Object[] arr = new String[]{"张三", "王五"};
System.out.println(arr.getClass().getCanonicalName());
arr[0] = 7;
Test.printArr(arr);
/*
正常编译
执行输出:
this is demo3
java.lang.String[]
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
at Test.demo3(Test.java:48)
at Test.main(Test.java:71)
*/
}
public static void printArr(Object[] arr) {
for (Object o : arr) {
System.out.print(o + " ");
}
System.out.println();
}
public static void main(String[]args){
//Test.demo1();
//Test.demo2();
Test.demo3();
}
}
输出截图:
分析过程详解:
第一步:
看ArrayList带Collection对象的构造函数源码(java.util.ArrayList):
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
看java.util.ArrayList,中toArray()源码:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
/**
* 返回 ArrayList 元素组成的数组
* @param a 需要存储 list 中元素的数组
* 若 a.length >= list.size,则将 list 中的元素按顺序存入 a 中,然后 a[list.size] = null, a[list.size + 1] 及其后的元素依旧是 a 的元素
* 否则,将返回包含list 所有元素且数组长度等于 list 中元素个数的数组
* 注意:若 a 中本来存储有元素,则 a 会被 list 的元素覆盖,且 a[list.size] = null
* @return
* @throws ArrayStoreException 当 a.getClass() != list 中存储元素的类型时
* @throws NullPointerException 当 a 为 null 时
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
// 若数组a的大小 < ArrayList的元素个数,则新建一个T[]数组,
// 数组大小是"ArrayList的元素个数",并将“ArrayList”全部拷贝到新数组中
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// 若数组a的大小 >= ArrayList的元素个数,则将ArrayList的全部元素都拷贝到数组a中。
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
可以看出,由于ArrayList中elementData类型为Object[],所以调用copyOf()返回值类型为Object[]。
第二步:
看 Arrays.asList()源码:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
仔细阅读官方文档,你会发现对 asList 方法的描述中有这样一句话:
返回一个由指定数组生成的固定大小的 List。
注意:参数类型是 T ,根据官方文档的描述,T 是数组元素的 class。
任何类型的对象都有一个 class 属性,这个属性代表了这个类型本身。原生数据类型,比如 int,short,long等,是没有这个属性的,具有 class 属性的是它们所对应的包装类 Integer,Short,Long。
asList 方法的参数必须是对象或者对象数组,而原生数据类型不是对象。当传入一个原生数据类型数组时,asList 的真正得到的参数就不是数组中的元素,而是数组对象本身。(解决方案:使用包装类数组。)
继续分析:
此时的ArrayList并非我们常用的java.util.ArrayList,而是Arrays的内部类。它继承自AbstractList,自然实现了Collection接口,代码如下:
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a; ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
} public int size() {
return a.length;
}
。。。。。。
}
ublic abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
。。。。。。。
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
。。。。。。
abstractList这个抽象类所定义的add和remove方法,仅仅是抛出了一个异常!
如果是想将一个数组转化成一个列表并做增加删除操作的话,建议代码如下:
public class Test {
public static void main(String[] args) {
String[] myArray = { "张三", "李四", "赵六" };
List<String> myList = new ArrayList<String>(Arrays.asList(myArray));
myList.add("王五");
}
};
demo2(测试代码中的):
public static void demo2(){
System.out.println("this is demo2");
List<String> list = Arrays.asList("张三", "王五");
Object[] arr=list.toArray();
System.out.println(arr.getClass().getCanonicalName());
arr[0]=new Object();
Test.printArr(arr);
/*
正常编译
执行输出:
this is demo2
java.lang.String[]
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Object
at Test.demo2(Test.java:31)
at Test.main(Test.java:55)
*/
}
上面的抛出异常分析:
asList方法直接将String[]数组作为参数传递给ArrayList的构造方法,然后将String[]直接赋值给内部的a,所以a的真实类型是String[],根据JLS规范String[]的clone方法返回的也是String[]类型。最终,toArray()方法返回的真实类型是String[],此时,操作arr[0]=new Object();是向数组中添加Object对象,就会报异常的问题了。
Jdk 6260652 Bug 问题是在2005年提出的,现在已经解决了,使用toArray(T[] a)避免Exception的发生,所以可能会导致类型不匹配的错误。
小总结:
Arrays.asList()的使用方法:
该方法是将数组转化为list。有以下几点需要注意:
1.该方法不适用于基本数据类型(byte,short,int,long,float,double,boolean)
解决方案:使用包装类数组,例子如下:
public class Test {
public static void main(String[] args) {
Integer[] myArray = { 1, 2, 3 };
List myList = Arrays.asList(myArray);
System.out.println(myList.size());
}
}
2.该方法将数组与列表链接起来,当更新其中之一时,另一个自动更新
3.不支持add和remove方法
将数组转化为一个List对象,一般会想到Arrays.asList()方法,这个方法会返回一个ArrayList类型的对象。但是用这个对象对列表进行添加删除更新操作,就会报UnsupportedOperationException异常。
原因:这个ArrayList类并非java.util.ArrayList类,而是Arrays类的静态内部类!
说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[]{"张三","王五"};
List list = Arrays.asList(str);
第一种情况:list.add("赵四"); //运行时异常
第二种情况:str[0] = "大二哈"; //list.get(0)也随着修改。
此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。 除非特别注明,否则如果指定数组引用为 null,则此类中的方法都会抛出 NullPointerException。
利用Jdk 6260652 Bug解析Arrays.asList的更多相关文章
- JDK 之 Arrays.asList - 源码分析
Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List . 其源代码如下: @SafeVarargs public static <T> Lis ...
- 【转】java.util.Arrays.asList 的用法
DK 1.4对java.util.Arrays.asList的定义,函数参数是Object[].所以,在1.4中asList()并不支持基本类型的数组作参数. JDK 1.5中,java.util.A ...
- Android 利用Gson生成或解析json
目前手机端和服务端数据交流格式一般是json,而谷歌提供了Gson来解析json.下载Gson:https://code.google.com/p/google-gson/ 下载的放在lib并导入,若 ...
- Amdroid示例:利用Gson生成或解析json
转自:http://www.cnblogs.com/liqw/p/4266209.html 目前手机端和服务端数据交流格式一般是json,而谷歌提供了Gson来解析json.下载Gson:https: ...
- Arrays.asList中所遇到的坑
前言 最近在项目上线的时候发现一个问题,从后台报错日志看:java.lang.UnsupportedOperationException异常 从代码定位来看,原来是使用了Arrays.asList() ...
- java.util.Arrays.asList 的小问题
JDK 1.4对java.util.Arrays.asList的定义,函数参数是Object[].所以,在1.4中asList()并不支持基本类型的数组作参数. JDK 1.5中,java.util. ...
- Arrays.asList(数组) 解说
最近在用Arrays的asList()生成的List时,List元素的个数时而不正确. Java代码 一:Arrays.asList(数组)该方法是将数组转化为集合(该方法主要用于Object对象数组 ...
- Arrays.asList的使用及异常问题
将数组转成List问题,通常我们习惯这样写成:List<String> list = Arrays.asList("1","2"); 于是我们这样就 ...
- 【转载】最近在用Arrays的asList()生成的List时,List元素的个数时而不正确,数组转化为List,即Arrays.asList(intArray);
最近在用Arrays的asList()生成的List时,List元素的个数时而不正确. Java代码 //经多次测试,只要传递的基本类型的数组,生成List的元素个数均为1 char arrc = { ...
随机推荐
- A JavaScript Image Gallery
childNodes property: The childNodes property is a way of getting information about the children of ...
- 同步锁之lock
一. synchronized的缺陷 当一个代码块被synchronized修饰时,同时该代码块被一个线程执行,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况: ...
- SSRS 制作报表时报错: 超时时间已到。在操作完成之前超时时间已过或服务器未响应。
转载注明出处,原文地址:http://www.cnblogs.com/zzry/p/5718739.html 在用ssrs 制作报表时报如下错误 错误信息截图: 看到如上错误第一个想到的解决方法就是 ...
- 6、CSS基础 part-4
1.CSS 定位属性 CSS 定位属性允许你对元素进行定位. 属性 描述 position 把元素放置到一个静态的.相对的.绝对的.或固定的位置中. top 定义了一个定位元素的上外边距边界与其包含块 ...
- linux环境搭建系列之memcached安装步骤
1.从官网在线下载最新的安装包 wget http://memcached.org/downloads/memcached-1.4.34.tar.gz 该命令为在线下载 注意:最新的地址会变动,所以最 ...
- 数据库——初始mysql语句(2)
sql语句 #1. 操作文件夹(库) 增:create database db1 charset utf8; 查:show create database db1; show databases; 改 ...
- Leetcode 594.最长和谐子序列
最长和谐子序列 和谐数组是指一个数组里元素的最大值和最小值之间的差别正好是1. 现在,给定一个整数数组,你需要在所有可能的子序列中找到最长的和谐子序列的长度. 示例 1: 输入: [1,3,2,2,5 ...
- BZOJ 1864:[Zjoi2006]三色二叉树(树DP)
三色二叉树 问题描述 输入 仅有一行,不超过500000个字符,表示一个二叉树序列. 输出 输出文件也只有一行,包含两个数,依次表示最多和最少有多少个点能够被染成绿色. 样例输入 1122002010 ...
- 本文将介绍“数据计算”环节中常用的三种分布式计算组件——Hadoop、Storm以及Spark。
本文将介绍“数据计算”环节中常用的三种分布式计算组件——Hadoop.Storm以及Spark. 当前的高性能PC机.中型机等机器在处理海量数据时,其计算能力.内存容量等指标都远远无法达到要求.在大数 ...
- linux系统——软链接、硬链接
区别:硬链接原文件&链接文件公用一个inode号,说明他们是同一个文件,而软链接原文件&链接文件拥有不同的inode号,表明他们是两个不同的文件: 在文件属性上软链接明确写出了是链接文 ...



