java集合數據複製到另外一個集合

Lsit中數據複製問題

這是由一道開放式面試題引發的文章,題目:加入內存足夠大,一個集合中有100萬條數據,怎麼高效的把集合中的數據複製到另外一個集合

1.1淺拷貝

java 中複製分爲淺拷貝和深拷貝

如果考察淺拷貝:直接使用clone方法

System.out.println("測試開始時");

        List<String> a=new ArrayList<String>(10000000);
        int i=1;
        for(int x = 1; x < 10000000; x = x+1){
            a.add(Integer.toString(x));
        }
        List<String> b=new ArrayList<String>(10000000);
        List<String> c=new ArrayList<String>(10000000);
        long begintime = System.nanoTime();

        b= (List<String>) ((ArrayList<String>) a).clone();  //淺拷貝

明顯,問題沒有這麼簡單

深度拷貝的話最簡單就是遍歷,這個基本都知道,自己實現遍歷性能不會是最高的,開始思考
collections包含的方法

Collections.copy(c,a);

但這種方法具有侷限,List c的size 要>= a.size()
並且根據代碼這種方式也是淺拷貝

接下來想,如果是List
可以使用addall()
原理是數據拷貝,使用了java的native方法複製內存
同樣也是淺拷貝

經過對比集中淺拷貝

java中提供了流式計算

 d = a.stream().collect(Collectors.toList());

經過實際驗證:
addall()和collections.copy(a,b)
消耗時間接近,是比較好的複製方法,其中addall()略微好於collections

一般java的高效的集合類都在

java.util.concurrent.*;

分析代碼:

long begintime5 = System.nanoTime();
        CopyOnWriteArrayList m=new CopyOnWriteArrayList(a);
        long endtime5 = System.nanoTime();
        long costTime5 = (endtime5 - begintime5)/1000;
        System.out.println("測試結束5: "+costTime5);

底層原理分析,看代碼

 /**
     * Creates a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection of initially held elements
     * @throws NullPointerException if the specified collection is null
     */
    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements;
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            elements = c.toArray();
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elements.getClass() != Object[].class)
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }

這段顯示,調用了Arrays.copuOf()

/**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of the class <tt>newType</tt>.
     *
     * @param <U> the class of the objects in the original array
     * @param <T> the class of the objects in the returned array
     * @param original the array to be copied
     * @param newLength the length of the copy to be returned
     * @param newType the class of the copy to be returned
     * @return a copy of the original array, truncated or padded with nulls
     *     to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException if <tt>original</tt> is null
     * @throws ArrayStoreException if an element copied from
     *     <tt>original</tt> is not of a runtime type that can be stored in
     *     an array of class <tt>newType</tt>
     * @since 1.6
     */
    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;
    }

接下來調用了

System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));

看看最後:

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

java的native 方法,效率最高
本質和collection的clone的方法邏輯一致

1.2深拷貝

集合的深度拷貝,除了遍歷還有一種是序列化和反序列,這種首先排除在外,接下來看看還有沒有其他方式

查詢了資料,木有發現更好的深拷貝方法

經過實踐對比,序列化、反序列化是效率最高的辦法

1.3 最終結論

  public static <T> List<T> depCopy(List<T> srcList) {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        try {
            ObjectOutputStream out = new ObjectOutputStream(byteOut);
            out.writeObject(srcList);

            ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
            ObjectInputStream inStream = new ObjectInputStream(byteIn);
            List<T> destList = (List<T>) inStream.readObject();
            return destList;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

以上針對問題,初步研究。

參考1:https://blog.csdn.net/u010648159/article/details/79144154
參考2:https://blog.csdn.net/demonliuhui/article/details/54572908

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章