zoukankan      html  css  js  c++  java
  • ArrayList使用toArray()构造数组时的问题

     标题:ArrayList使用toArray()构造数组时的问题

    关键字:toArray  构造数组

    作者:jrq

    摘要:解决使用ArrayList.toArray()构造数组时的问题。做备忘。

    本文链接: http://blog.csdn.net/jrq/archive/2005/10/27/517428.aspx

    正文:

    1. 为了方面按列作外循环,想把ArrayList构造成一个二维数组,如下:

        ......

      ArrayList result=GetResult();

      int n=result.size();

      String[][] myArray=new String[n][]; //定义二维数组
     
      for (int i=0;i<n;i++)  //构造二维数组
      {
        ArrayList tempArray= (ArrayList)result.get(i);
        myArray[i]=(String[])tempArray.toArray(); 
      }

      ......

    程序可以编译通过。

    但在运行到myArray[i]=(String[])tempArray.toArray()时,出现java.lang.ClassCastException的错误,很奇怪。

    花了一晚上时间,查了N多资料,总算搞定了。现把问题记录下来,以备参考。

    2. 此事从头说起。  

    ArrayList类扩展AbstractList并执行List接口。ArrayList支持可随需要而增长的动态数组。

        ArrayList有如下的构造函数:
      
            ArrayList( )
            ArrayList(Collection c)
            ArrayList(int capacity)

    如果调用new ArrayList()构造时,其默认的capacity(初始容量)为10。

    参见ArrayList源码,其中是这样定义的:

        public ArrayList() {
      this(10);
         }

    默认初始化内部数组大小为10。为什么是10?不知道。可能SUN觉得这样比较爽吧。

    程序编译后执行ArrayList.toArray(),把ArrayList转化为数组时,该数组大小仍为capacity(为10)。

    当装入的数据和capacity值不等时(小于capacity),比如只装入了5个数据,数组中后面的(capacity - size)个对象将置为null,此时当数组强制类型转换时,容易出现一些问题,如java.lang.ClassCastException异常等。

    解决办法是:在用ArrayList转化为数组装数据后,使用trimToSize()重新设置数组的真实大小。


    3. 本例修改后的代码修如下,可顺利运行:

        for (int i=0;i<n;i++)  //构造二维数组
          {
                ArrayList tempArray= (ArrayList)result.get(i);
                myArray[i]=(String[])tempArray.toArray(new String[0]);   //注意此处的写法
           }

     看看下面这些也许就明白了--

    ArrayList.toArray()之一:

    public Object[] toArray() {
      Object[] result = new Object[size];
      System.arraycopy(elementData, 0, result, 0, size);
      return result;
    }

    返回ArrayList元素的一个数组,注意这里虽然生成了一个新的数组,但是数组元素和集合中的元素是共享的,Collection接口中说这个是安全的,这是不严格的。

    下面的例子演示了这个效果。
     
       ArrayList al=new ArrayList();
       al.add(new StringBuffer("hello"));
       Object[] a=al.toArray();
       StringBuffer sb=(StringBuffer)a[0];
       sb.append("changed");  //改变数组元素同样也改变了原来的ArrayList中的元素
       System.out.println(al.get(0));    

    这里不要用String来代替StringBuffer,因为String是常量。


    ArrayList.toArray()之二:

    public Object[] toArray(Object a[]) {
      if (a.length < size)
        a = (Object[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
       System.arraycopy(elementData, 0, a, 0, size);
       if (a.length > size)
          a[size] = null;
       return a;
    }

    这个方法有可能不需要生成新的数组,注意到如果数组a容量过大,只在size处设置为null。

    如果这个数组a足够大,就会把数据全放进去,返回的数组也是指向这个数组,(数组多余的空间存储的是null对象);要是不够大,就申请一个跟参数同样类型的数组,把值放进去,然后返回。


    4. 网上的资料一:

      public String[] getPlatformIDList()
      
      {
            Vector result = new Vector();
            try
            {
                Statement stmt = conn.createStatement();
                String sql = "SELECT PlatformID FROM Platform";
                rs = stmt.executeQuery(sql);
                while(rs.next())
                {
                    result.add(rs.getString(1));
                }        
                if (result.size() > 0)
                {
                    String[] str = (String[]) result.toArray(); // 出现ClassCastException
                    return str;
                }
                else
                    return null;
            }
            catch(Exception e)
            {
                System.err.println(e);
                return null;
            }
            finally
            {
                try
                {
                    rs.close();
                    conn.close();
                }
                catch(Exception e2)
                {}
            }
        }

        程序运行后,发现不能将Vector类经过toArray()方法得到的Object[]直接转换成String[]。

        找到用另一个带有参数的 toArray(T[] a)方法才可以。

        将该语句改为:

        String[] str = (String[]) result.toArray(new String[1]);即告诉Vector,我要得到的数组的类型。

        回想一下,应该是java中的强制类型转换只是针对单个对象的,想要偷懒,将整个数组转换成另外一种类型的数组是不行的。


    5. 网上的资料二:

        正确使用List.toArray()--
      
       在程序中,往往得到一个List,程序要求对应赋值给一个array,可以这样写程序:
     
        Long [] l = new Long[list.size()];
        for(int i=0;i<list.size();i++)
            l[i] = (Long) list.get(i);
     
        要写这些code,似乎比较繁琐,其实List提供了toArray()的方法。
        但是要使用不好,就会有ClassCastExceptiony异常。究竟这个是如何产生的,且看代码:

            List list = new ArrayList();
            list.add(new Long(1));list.add(new Long(2));
            list.add(new Long(3));list.add(new Long(4));
            Long[] l = (Long[])list.toArray();
            for(int i=0; i<l.length; i++)
                System.out.println(l[i].longValue());

        红色代码会抛java.lang.ClassCastException。

        当然,为了读出值来,你可以这样code:

            Object [] a =  list.toArray();
            for(int i=0;i<a.length;i++)
                System.out.println(((Long)a[i]).longValue());

        但是让数组丢失了类型信息,这个不是我们想要得。


        toArray()正确使用方式如下:

            1)  Long[] l = new Long[<total size>];
                  list.toArray(l);
     
            2)  Long[] l = (Long []) list.toArray(new Long[0]);

            3)  Long [] a = new Long[<total size>];
                  Long [] l = (Long []) list.toArray(a);

    6. 总结补充:

          java sdk doc 上讲:
     
          public Object[] toArray(Object[] a)

          a--the array into which the elements of this list are to be stored, if it is big enough; otherwise, a new array of the same  runtime type is allocated for this purpose.

          如果这个数组a足够大,就会把数据全放进去,返回的数组也是指向这个数组,(数组多余的空间存储的是null对象);要是不够大,就申请一个跟参数同样类型的数组,把值放进去,然后返回。
     
         需要注意的是:你要是传入的参数为9个大小,而list里面有5个object,那么其他的四个很可能是null , 使用的时候要特别注意。

    7. 完毕。

     

                                                                                                                  J.R.Q.

                                                                                                        2005.10.27凌晨于广州 

  • 相关阅读:
    java.lang.NoSuchMethodError
    asm相关内容想下载(包括 jar 包)
    Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/objectweb/asm/Type
    用Navicat连接mysql报错:2003-Can't connect to MySql server on '10.100.0.109'(10039)
    The type java.lang.reflect.AnnotatedElement cannot be resolved. It is indirectly referenced from required .class files
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
    交通测速方式
    卡口和电子警察的区别
    Myeclipse连接Mysql数据库时报错:Error while performing database login with the pro driver:unable
    在window上安装mysql
  • 原文地址:https://www.cnblogs.com/jackrex/p/3001330.html
Copyright © 2011-2022 走看看