首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 系统运维 >

动态数组:java.lang.System上的arraycopy和java.util.Arrays.copyOf方法

2012-09-09 
动态数组:java.lang.System下的arraycopy和java.util.Arrays.copyOf方法java.lang.System下的arraycopy和j

动态数组:java.lang.System下的arraycopy和java.util.Arrays.copyOf方法
java.lang.System下的arraycopy和java.util.Arrays.copyOf方法

(1) java.lang.System.arraycopy

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length); 


问题:方法没有任何的实现,具体是如何实现的呢?

以下是关于该方法具体的说明:

* @param      src      the source array.    * @param      srcPos   starting position in the source array.    * @param      dest     the destination array.    * @param      destPos  starting position in the destination data.    * @param      length   the number of array elements to be copied.    * @exception  IndexOutOfBoundsException  if copying would cause    *               access of data outside array bounds.    * @exception  ArrayStoreException  if an element in the <code>src</code>    *               array could not be stored into the <code>dest</code> array    *               because of a type mismatch.    * @exception  NullPointerException if either <code>src</code> or    *               <code>dest</code> is <code>null</code>.    [b](2) java.util.Arrays.copyOf  [/b]  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) {        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;    }  



由上述的源码不难看出两者的最大区别是:

arraycopy 方法会因为新数组大小比久数组大小小而报IndexOutOfBoundsException

copyOf 则不会因此报错,因为copyOf 的返回值是在内部new 好的copy 数组,而该copy 数组new 的大小就等于newLength ,

    故即使在客户端指定好了新数组newArray 的大小,接收到返回值后也是指向底层new 出来的数组copy 。换句话说( 也可以因此推出其他的区别) ,在客户端代码中即使不给新数组new 对象,如:String[] newStr = null;

    那么对于arraycopy 是会报NullPointerException 的错误的,而对于java.util.Arrays 中的copyOf 方法则由于jdk 底层

    已经new 出了对象而不会报该错误!不过需要特别注意的是:copyOf 方法最后也是调用System.arraycopy 的方法,不过由于前面的准备,异常情况就不会出现了。

下面是一个具体实例:

package com.yilong.array.copyof;    public class ArrayCopyOf {        public static void main(String[] args) {            String[] oldStr = new String[6];            setValues(oldStr);            String[] newStr = null;            newStr = java.util.Arrays.copyOf(oldStr, 6);            //System.arraycopy(oldStr, 0, newStr, 0, oldStr.length);            // 会报错NullPointerException ,如果改为newStr = new String[5],            // 那么调用Arrays.copyOf 方法不会报错,而调用System.arraycopy 方法            // 则回报IndexOutOfBoundsException            print(oldStr);            print(newStr);            System.out.println(oldStr.length);            System.out.println(newStr.length);        }        private static void print(String[] newStr) {            // TODO Auto-generated method stub            for (int i = 0; i < newStr.length; i++) {                System.out.print(newStr[i] + " : ");            }            System.out.println();        }             private static void setValues(String[] oldStr) {            // TODO Auto-gemmnerated method stub            for (int i = 0; i < oldStr.length; i++) {                oldStr[i] = "str " + i;            }        }    }  


与此同时,java.util.Arrays 还重载了很多copyOf 方法:

public static byte[] copyOf(byte[] original, int newLength) {            byte[] copy = new byte[newLength];            System.arraycopy(original, 0, copy, 0,                             Math.min(original.length, newLength));            return copy;        }    public static int[] copyOf(int[] original, int newLength) {            int[] copy = new int[newLength];            System.arraycopy(original, 0, copy, 0,                             Math.min(original.length, newLength));            return copy;        }  



等等;

现在再来讨论下ArrayList 中对该两个方法的使用情况:

由于ArrayList 底层是用数组实现的,那么就必然会面临两个问题:

(1) 一开始必须要指定一个初始化的数组大小;

java.util.ArrayList 中初始化ArrayList 大小是10 :

private transient Object[] elementData;// 为什么要设置成transient? 不想被序列化?    public ArrayList(int initialCapacity) {        super();         if (initialCapacity < 0)         throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);        this.elementData = new Object[initialCapacity];    }    public ArrayList() {        this(10);    }  

 
(2) 当数组容量不够时,能够动态增加容量。 
 
java.util.ArrayList 中的add(E e) 方法 
 
public boolean add(E e) {        ensureCapacity(size + 1);  // Increments modCount!!        elementData[size++] = e;        return true;    }    public void ensureCapacity(int minCapacity) {        modCount++;        int oldCapacity = elementData.length;        if (minCapacity > oldCapacity) {            Object oldData[] = elementData;            int newCapacity = (oldCapacity * 3)/2 + 1;            if (newCapacity < minCapacity)                        newCapacity = minCapacity;          // minCapacity is usually close to size, so this is a win:          elementData = Arrays.copyOf(elementData, newCapacity);        }    } 



可见,每次添加元素时,JDK 都会先检查数组elementData 的容量是否已经满了,如果满了,就会调用Arrays.copyOf 方法,用elementData 中的元素和新数组的长度构造一个新的数组,并重新赋值给elementData 数组。此处不难看出,由于需求是把长度已经增长了的elementData 数组重新赋值给elementData ,故直接使用System.arraycopy 方法是不适合的。

另外,新数组长度的改变也是有讲究的,JDK 的设计是每次有新的扩展数组长度的需要到来时,就按int newCapacity = (oldCapacity * 3)/2 + 1; 的算法构造新数组的长度,不难看出,这种算法构造出来的新的数组长度的增量都会比上一次大( 而且是越来越大) ,即认为客户需要增加的数据很多,而避免频繁newInstance 的情况。

下面再来看ArrayList 中的toArray 方法
 
public Object[] toArray() {      return Arrays.copyOf(elementData, size);    }    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;        } 



此处主要看第二个toArray 方法,可见,客户是想把ArrayList 中的元素转换成数组并存放到指定的a 数组中,因此就要判断a.length 与elementData 当前的大小了,如果a.length<size, 那么调用System.arraycopy 方法是肯定会报错的,故必须调用Arrays.copyOf 方法,而如果a.length>=size 那么就比不上调用System.arraycopy 方法来得方便了( 因为根据上面说的,Arrays.copyOf 方法最终也是调用System.arraycopy 方法的) 。

综上所述,可以总结:在允许的情况下,尽量调用System.arraycopy 方法,实在不行再调用Arrays.copyOf 方法。

Simple Stack push method

private String[] buffer = new String[100];    public final void push(String goods) {          if (point + 2 > buffer.length) {              // System.arraycopy is better than Arrays.copyOf              // buffer = Arrays.copyOf(buffer, buffer.length + INCREMENT);              String[] newBuffer = new String[(buffer.length * 3) / 2 + 1];              System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);              buffer = newBuffer;          }          point++;          buffer[point] = goods;          System.out.println("buffer size ========================" + buffer.length);          System.out.println("point ========================" + point);      }  



如果不采用动态数组策略,将会抛:
Exception in thread "****producer***" java.lang.ArrayIndexOutOfBoundsException: 100  

热点排行