java 集合 深度复制多种实现方式和使用注意事项

今天遇到一个问题:对一个集合进行深度复制
最先想到的是利用集合工具类的copy()方法,但是它出现了一些问题

Collections.copy使用注意事项

示例代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CollectionsTest {
	public static void main(String[] args) {
		// 创建一个集合list
		String[] array = { "1", "2", "3", "4", "5" };
		List<String> list = Arrays.asList(array);

		// 创建一个新的集合dest
		List<String> dest = new ArrayList<String>();
		// 把list集合中的元素复制到dest集合中
		Collections.copy(dest, list);

		System.out.println(dest);
	}
}

运行结果:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Source does not fit in dest
	at java.util.Collections.copy(Collections.java:556)
	at com.CollectionsTest.main(CollectionsTest.java:17)

源码分析:

	 /**
     * Copies all of the elements from one list into another.  After the
     * operation, the index of each copied element in the destination list
     * will be identical to its index in the source list.  The destination
     * list must be at least as long as the source list.  If it is longer, the
     * remaining elements in the destination list are unaffected. <p>
     *
     * This method runs in linear time.
     *
     * @param  <T> the class of the objects in the lists
     * @param  dest The destination list.
     * @param  src The source list.
     * @throws IndexOutOfBoundsException if the destination list is too small
     *         to contain the entire source List.
     * @throws UnsupportedOperationException if the destination list's
     *         list-iterator does not support the <tt>set</tt> operation.
     */
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                di.set(si.next());
            }
        }
    }

在这里插入图片描述

结论: dest.size()的长度不能小于srcSize();目标列表必须至少与源列表一样长。如果更长,则目标列表中的其余元素不受影响。

修改代码,对dest初始列表容量:

public class CollectionsTest {
	public static void main(String[] args) {
		// 创建一个集合list
		String[] array = { "1", "2", "3", "4", "5" };
		List<String> list = Arrays.asList(array);

		// 创建一个新的集合dest
		List<String> dest = new ArrayList<String>(5);
		// 把list集合中的元素复制到dest集合中
		Collections.copy(dest, list);

		System.out.println(dest);
	}
}

运行结果:
在这里插入图片描述
问题依旧出现,当打印dest的size,结果为 0

分析ArrayList源码:

	/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

结论:构造具有指定初始容量的空列表,实际上没有定义元素,元素数量为0

解决方案:

一个长度是原集合大小的数组,元素的个数是size,元素初始值为null。

public class CollectionsTest {
	public static void main(String[] args) {
		// 创建一个集合list
		String[] array = { "1", "2", "3", "4", "5" };
		List<String> list = Arrays.asList(array);

		// 创建一个新的集合dest
		List<String> dest = Arrays.asList(new String[list.size()]);
		// 把list集合中的元素复制到dest集合中
		Collections.copy(dest, list);

		System.out.println(dest);
	}
} 

运行结果:
[1, 2, 3, 4, 5]

Arrays.asList()使用注意事项

Arrays.asList()
Arrays是java容器相关操作的工具类,asList()方法将数组转换为集合。

在使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,否则会抛出UnsupportOperationException异常。

示例代码:

public class CollectionsTest {
	public static void main(String[] args) {
		// 创建一个集合list
		String[] array = { "1", "2", "3", "4", "5" };
		List<String> list = Arrays.asList(array);

		// 创建一个新的集合dest
		List<String> dest = Arrays.asList(new String[list.size()]);
		// 把list集合中的元素复制到dest集合中
		Collections.copy(dest, list);

		dest.remove("1");

		System.out.println(dest);
	}
}

运行结果:

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.remove(AbstractList.java:161)
	at java.util.AbstractList$Itr.remove(AbstractList.java:374)
	at java.util.AbstractCollection.remove(AbstractCollection.java:293)
	at com.CollectionsTest.main(CollectionsTest.java:19)

源码分析:

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) {
        a = Objects.requireNonNull(array);
    }

    @Override
    public int size() {
        return a.length;
    }

    @Override
    public Object[] toArray() {
        return a.clone();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        int size = size();
        if (a.length < size)
            return Arrays.copyOf(this.a, size,
                                 (Class<? extends T[]>) a.getClass());
        System.arraycopy(this.a, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

    @Override
    public E get(int index) {
        return a[index];
    }

    @Override
    public E set(int index, E element) {
        E oldValue = a[index];
        a[index] = element;
        return oldValue;
    }

    @Override
    public int indexOf(Object o) {
        E[] a = this.a;
        if (o == null) {
            for (int i = 0; i < a.length; i++)
                if (a[i] == null)
                    return i;
        } else {
            for (int i = 0; i < a.length; i++)
                if (o.equals(a[i]))
                    return i;
        }
        return -1;
    }

    @Override
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }

    @Override
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator(a, Spliterator.ORDERED);
    }

    @Override
    public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        for (E e : a) {
            action.accept(e);
        }
    }

    @Override
    public void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        E[] a = this.a;
        for (int i = 0; i < a.length; i++) {
            a[i] = operator.apply(a[i]);
        }
    }

    @Override
    public void sort(Comparator<? super E> c) {
        Arrays.sort(a, c);
    }
}

结论:
由Arrays.asList() 返回的是Arrays的内部类ArrayList, 而不是java.util.ArrayList。
Arrays的内部类ArrayList和java.util.ArrayList都是继承AbstractList抽象类,都包含:remove、add等方法。
AbstractList中默认实现throw UnsupportedOperationException而且不作任何操作。
java.util.ArrayList重新实现了这些方法
Arrays的内部类ArrayList没有重新,所以会抛出异常。

解决方案:
参照下方使用构造方法复制

其他集合深度复制方法:

  • 遍历循环复制
  • 使用构造方法复制
    List<String> dest = new ArrayList<String>(list);

posted on 2020-04-13 16:11  疯狂的小萝卜头  阅读(1371)  评论(0编辑  收藏  举报