zoukankan      html  css  js  c++  java
  • 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);
  • 相关阅读:
    RocketMQ同一个消费者唯一Topic多个tag踩坑经历
    1.写一个字符串反转函数.
    1.什么是灰度发布?
    简单比较 @EnableEurekaClient 和 @EnableDiscoveryClient 两个注解
    字符串拼接出现null的问题
    webSocket无法注入bean问题解决方案
    数据库中的时间类型需要指定长度吗?
    SQL语句的优化
    NoClassDefFoundError
    1.代码规范之 if 语句编写
  • 原文地址:https://www.cnblogs.com/gmhappy/p/13457033.html
Copyright © 2011-2022 走看看