`

动态数组: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  
分享到:
评论

相关推荐

    System.arraycopy和Arrays.copyOf

    个人研究所得,包含对其内部jdk源码的分析。 同时会结合ArrayList中对该两个方法的调用做进一步说明。...总结一句话:在允许的情况下,尽量调用System.arraycopy方法,实在不行再调用Arrays.copyOf方法。

    JavaSE 基础 数组.pdf

    ● 复制数组:使用System.arraycopy()方法或Arrays.copyOf()方法将一个数组复制到另一个数组中。 ● 排序数组:使用Arrays.sort()方法对数组进行排序。 ● 搜索数组:使用Arrays.binarySearch()方法在一个已排序的...

    System.arraycopy实现数组之间的复制

    System提供了一个静态方法arraycopy(),我们可以使用它来实现数组之间的复制。

    java学习笔记 - 6

    数组的复制:System.arraycopy(src, srcPos, dest, destPos, length); Arrays.copyOf(original, newLength); 数组的排序:排序方法和排序分类 Arrays.sort(arr);//对arr进行升序排列 2.方法(函数、过程): ...

    使用System.arraycopy()实现数组之间的复制

    使用System.arraycopy()实现数组之间的复制

    Java基础之数组拷贝

    文章目录1. 直接赋值2....(注:如果想打印数组内的所有值,可以使用Arrays.toString(x)方法,但需import java.util.Arrays) int[] originalArray = { 1, 2, 3, 4, 5 }; int[] newArray = originalArray

    java数组扩容2

    Java数组扩容的原理  1)Java数组对象的大小是固定... 3)System.arraycopy()可以复制数组。  4)Arrays.copyOf()可以简便的创建数组副本。  5)创建数组副本的同时将数组长度增加就变通的实现了数组的扩容。

    java培训教程教学课件

    System.arraycopy(ia,0,ib,0,3); // 复制源数组中从下标0开始的3个元素到目的数组,从下标0的位置开始存储。 for(int i=0;i&lt;ia.length;i++) System.out.print(ia[i]); System.out.println(); for(int j=0;...

    JAVA集合系列(4):关于数组复制的几种方法(源码解读)及执行效率,我们知多少?

    目录 前言 1、Arrays.copyOf() 2、System.arraycopy() 3、Arrays.copyOfRange() ...在 Java 中实现数组复制有 5 种方法: 【1】Arrays 类的 copyOf() 方法 【2】System 类的 arraycopy() 方法 【3】Arrays类的copyO

    【java演示】什么是数组?数据结构(二)

    java的数组复制方法System.arraycopy()的使用说明 数组:随机读取,顺序存储 1. 读取数据 int array = {1,2,3,4,5} array[index] 2. 更新元素 int array = {1,2,3,4,5} array[index]=newValue 数组读取元素和更新...

    数组的深拷贝与浅拷贝以及数组拷贝的几种方式比较

    目录一、深拷贝与浅拷贝解析浅拷贝深拷贝二、数组拷贝的方式1.for循环来拷贝2.System.arraycopy( )拷贝3.Arrays.copyOf( )拷贝4.clone( )拷贝5.解释三、四种拷贝方式效率比较1. System.arraycopy( )2.Arrays.copyOf...

    JAVA——linux

    import java.util.Arrays; import java.util.Random; public class DoubleBall { public static void main(String[] args) { System.out.println(create()); } public static String create(){ String[] ...

    2个数组指向同一个空间.txt

    的 arraycopy 方法来完成。 同样当数组作为方法的参数传递时,也是传递的对于数组的引用,因此在方法中对数组 进行的操作会影响到原来的数组。例如: public void changeArray(int[] a){ for (int i=0;i&lt;a....

    java中两个byte数组实现合并的示例

    今天在于硬件进行交互的过程中,要到了了需要两个数组进行合并,然后对数组进行反转和加密操作,以下是两个byte数组合并的方法。 /** * * @param data1 * @param data2 * @return data1 与 data2拼接的结果 */ ...

    实现数组复制常用的5种方式及组合类的深复制(深拷贝)

    在Java语言中,一般会采用下面的几种方法进行数组的复制。 a) for循环逐一复制; b) System.arraycopy()方法 c) Arrays.copyOf()方法 d) 使用clone()方法 e) Arrays.copyOfRange()方法 接下来,我们看下这几种方法...

    90行Java代码实现最简洁小巧俄罗斯方块~~全球最小实现:)

    90行Java代码实现小巧俄罗斯方块~~ 66行改为: System.arraycopy(matrix[j-1],0,matrix[j],0,10); 修改的一个Bug 最新版本在:http://wireless.javaeye.com/blog/595321

    javacard applet 开发实例 正常运行 带jar包

    javacard.framework.Util.arrayCopy(buf, (short)8, send, (short)0, AmbiqConstant.BYTES_PER_BLOCK); } else { ISOException.throwIt(AmbiqConstant.SW_WRONG_S8); } i= ...

    Java字符串及数组操作的实例源码.zip

    ArrayCopy ArrayOfArraysArrayOfArrays2 ArrayOfStrings ArraySort CharacterOp charAtOp StringBufferOp StringOp CommandPara m

Global site tag (gtag.js) - Google Analytics