Java集合 -- 疑难点总结(Arrays.asList()正确使用Collection.toArray()正确使用反转数组 foreach 循环不要进行元素的 remove/add 操作)

Posted CodeJiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java集合 -- 疑难点总结(Arrays.asList()正确使用Collection.toArray()正确使用反转数组 foreach 循环不要进行元素的 remove/add 操作)相关的知识,希望对你有一定的参考价值。

文章目录

1. Arrays.asList()正确使用


1.1 简介

Arrays.asList()在平时开发中还是比较常见的,我们可以使用它将一个数组转换为一个List集合

示例:

        String[] myArray = "Apple", "Banana", "Orange";
        List<String> myList = Arrays.asList(myArray);
        System.out.println(myList);

结果:

上面两个语句等价于下面一条语句

 List<String> myList = Arrays.asList("Apple", "Banana", "Orange");

JDK 源码对于这个方法的说明:

/** *返回由指定数组支持的固定大小的列表。
此方法作为基于数组和基于集合的API之间的桥梁,
与 Collection.toArray()结合使用。返回的List是可序列化并实现
RandomAccess接口。 */
 public static <T> List<T> asList(T... a)  
	 return new ArrayList<>(a); 
 

1.2 《阿里巴巴Java 开发手册》对其的描述


1.2.1 第一种情况

示例:

        List<String> myList = Arrays.asList("Apple", "Banana", "Orange");
        myList.add("Peach");

结果:


1.2.2 第二种情况

示例:

        String[] myArray = "Apple", "Banana", "Orange";
        List<String> myList = Arrays.asList(myArray);
        myArray[0] = "Durian";
        System.out.println(myList);

结果:


1.3 Arrays.asList()使用时的注意事项总结


1.3.1 传递的数组必须是对象数组,而不是基本类型。

Arrays.asList()泛型方法,传入的对象必须是对象数组。

示例:

        int[] myArray = 1, 2, 3;
        List myList = Arrays.asList(myArray);
        System.out.println(myList.size());//1
        System.out.println(myList.get(0));//数组地址值
        System.out.println(myList.get(1));//报错:ArrayIndexOutOfBoundsException
        int[] array = (int[]) myList.get(0);
        System.out.println(array[2]);//3

当传入一个原生数据类型数组时,Arrays.asList() 的真正得到的参数就不是数组中的元素,而是数组对象本身!此时List 的唯一元素就是这个数组,这也就解释了上面的代码。


1.3.2 使用包装类型数组解决问题

        Integer[] myArray = 1, 2, 3;
        List myList = Arrays.asList(myArray);
        System.out.println(myList.size());// 3
        System.out.println(myList.get(0));// 1
        System.out.println(myList.get(1));// 2

1.3.3 使用集合的修改方法:add()、remove()、clear()会抛出异常


1.3.4 Arrays.asList() 方法返回的并不是 java.util.ArrayList

Arrays.asList() 方法返回的并不是 java.util.ArrayList ,而是 java.util.Arrays 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。

示例:

        List myList = Arrays.asList(1, 2, 3);
        System.out.println(myList.getClass());//class java.util.Arrays$ArrayList

结果:

下图是java.util.Arrays$ArrayList的源码,我们可以看到这个类重写的方法有哪些。

    /**
     * @serial include
     */
    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);
        
    

我们再看一下java.util.AbstractListjava.util.Arrays$ArrayList的父类)的remove()方法,这样我们就明白为啥会抛出UnsupportedOperationException异常。


1.4 正确的将数组转换为ArrayList


1.4.1 自己动手实现(学习阶段)

示例:自定义方法

    static <T> List<T> arrayToList(final T[] array) 
        final List<T> arrayList = new ArrayList<T>(array.length);
        
        for (final T s : array) 
    		arrayList .add(s);
		
        return (arrayList);
    

使用:

        Integer[] myArray = 1, 2, 3;
        List<Integer> integerList = arrayToList(myArray);
        System.out.println(integerList);// [1, 2, 3]
        System.out.println(integerList.getClass());//class java.util.ArrayList

结果:


1.4.2 最简便的方法(推荐)

List list = new ArrayList<>(Arrays.asList("a", "b", "c"))

1.4.3 使用 Java8 的Stream(推荐)


1.4.4 使用 Guava(推荐)

不可变集合:

对于不可变集合,你可以使用ImmutableList类及其of()copyOf()工厂方法:(参数不能为空)

可变集合:

对于可变集合,你可以使用Lists类及其newArrayList()工厂方法:


1.4.5 list自带的addAll方法

示例代码:

        String[] strs = new String[]"H", "E", "L", "L", "O";
        final List<String> arrayList = new ArrayList<>(strs.length);
        arrayList.addAll(Arrays.asList(strs));

        System.out.println(arrayList);

运行结果:


1.4.6 使用 Apache Commons Collections

先导入相关依赖:

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>

测试代码:

        String[] str = "a", "b", "c";
        
        List<String> list = new ArrayList<>();
        CollectionUtils.addAll(list, str);
        
        System.out.println(list);

结果:


2. Collection.toArray()正确使用、反转数组

该方法是一个泛型方法:<T> T[] toArray(T[] a); 如果toArray方法中没有传递任何参数的话返回的是Object类型数组。

由于JVM优化,new String[0]作为Collection.toArray()方法的参数现在使用更好,new String[0]就是起一个模板的作用,指定了返回数组的类型0是为了节省空间,因为它只是为了说明返回的类型。

代码:

        String[] s = new String[]"dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A";
        List<String> list = Arrays.asList(s);
        
        Collections.reverse(list);// 反转数组
        s = list.toArray(new String[0]);//没有指定类型的话会报错 java.lang.Object[]无法转换为java.lang.String[]

        System.out.println(Arrays.toString(s));

结果:


3. 不要在 foreach 循环里进行元素的 remove / add 操作

如果要进行remove操作,可以调用迭代器的 remove 方法而不是集合类的 remove 方法。因为如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身remove / add方法,迭代器都将抛出一个ConcurrentModificationException,这就是单线程状态下产生的 fail-fast 机制。

fail-fast 机制 :多个线程对 fail-fast 集合进行修改的时,可能会抛出ConcurrentModificationException(并发修改异常),单线程下也会出现这种情况,上面已经提到过。

java.util包下面的所有的集合类都是fail-fast的,而java.util.concurrent包下面的所有的类都是fail-safe的。


3.1 正确示例

示例:

        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");

        Iterator<String> iterator = list.iterator();

        while (iterator.hasNext()) 
            String item = iterator.next();
            if (item.equals("2")) 
                // 删除当前的item元素
                iterator.remove();
            
        

        System.out.println(list);

运行结果:


3.2 错误示例

代码:

        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");


        for (String item : list) 
            if ("1".equals(item)) 
                list.remove(item);
            
        

        System.out.println(list);

运行结果:



以上是关于Java集合 -- 疑难点总结(Arrays.asList()正确使用Collection.toArray()正确使用反转数组 foreach 循环不要进行元素的 remove/add 操作)的主要内容,如果未能解决你的问题,请参考以下文章

java基础疑难点总结之成员变量的继承,方法重载与重写的区别,多态与动态绑定

Java基础 -- 疑难点总结(equls的正确姿势整型包装类值的比较BigDecimal的正确姿势基本数据类型与包装数据类型的使用标准)P3C插件(阿里规范插件)

Hibernate 二级缓存疑难点

数组与集合互转

HTML常见面试题及疑难点解答

java基础知识疑难点