美文网首页
【Java 开发常见的坑】—— Arrays 使用

【Java 开发常见的坑】—— Arrays 使用

作者: 爱打乒乓的程序员 | 来源:发表于2020-10-27 23:38 被阅读0次

    日常开发中,Arrays工具类十分常用,但如果对Arrays的源码不熟悉的话,就有可能踩到坑。
    以下是我日常开发或身边同事遇到关于Arrays工具类的坑:

    • 使用Arrays.asList转换为List数组,并对原始的对象修改
    • 使用Arrays.asList转换为List数组,并对List数组进行增删改操作
    • 使用Arrays.asList转换基本类型对象

    踩坑1.使用Arrays.asList转换为List数组,并对原始的对象修改

    实例:

    public class JavaTestApplication {
        public static void main(String[] args) {
            // 原始数组对象
            String[] strArray = {"H", "e", "l", "l", "o"};
            List<String> stringList = Arrays.asList(strArray);
            System.out.println(stringList);
            strArray[0] = "666";
            System.out.println(stringList);
        }
    }
    

    输出结果:

    [H, e, l, l, o]
    [666, e, l, l, o]
    

    可以发现,使用Arrays.asList将原来的数组对象转换为list对象后,改变原来的数组对象,list对象也会一同的修改!

    深入源码一探究竟!

        public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }
    

    嗯?怎么和我平常使用new ArrayList()创建对象不太一样?比如平常创建ArrayList对象的时候,一般都是通过new ArrayList()new ArrayList(Collection c),但使用Arrays.asList转换的时候,参数却是一个数组对象,而不是集合类型。莫非是Arrays类的ArrayList对象和java.util.ArrayList不一样?

    打开 Arrays.asList 方法中的 ArrayList 对象,居然是Arrays内的内部类!


        private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable {
            private static final long serialVersionUID = -2764017481108945198L;
            private final E[] a;
    
            // 直接将参数的数组对象赋值给Arrays.ArrayList的对象
            ArrayList(E[] array) {
                a = Objects.requireNonNull(array);
            }
            
            // 先省略其他方法
        }
    

    通过源码终于知道示例中为什么修改原始的数组会影响转换后的List对象,因为都是共享同一个数组对象呀(也就是说共享同一块内存)!

    既然知道问题所在,那就容易解决啦!

    public class JavaTestApplication {
        public static void main(String[] args) {
            String[] strArray = {"H", "e", "l", "l", "o"};
            List stringList = new ArrayList(Arrays.asList(strArray));
            System.out.println(stringList);
            strArray[0] = "666";
            System.out.println(stringList);
        }
    }
    

    输出结果:

    [H, e, l, l, o]
    [H, e, l, l, o]
    

    结果是达到预想的情况,其实就是通过new一个ArrayList对象,新建一个ArrayList对象的话,就不会共享同一块内存啦!

    踩坑2.使用Arrays.asList转换为List数组,并对List数组进行增删改操作

    那现在的使用场景,如果我只需要通过Arrays.asList转换为List集合后,对转换后的集合进行增删改操作,那下面的代码会由问题吗?

    public class JavaTestApplication {
        public static void main(String[] args) {
            String[] strArray = {"H", "e", "l", "l", "o"};
            List stringList = Arrays.asList(strArray);
            System.out.println(stringList);
            stringList.add("666");
            System.out.println(stringList);
        }
    }
    

    输出结果:

    [H, e, l, l, o]
    Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.AbstractList.add(AbstractList.java:148)
        at java.util.AbstractList.add(AbstractList.java:108)
        at com.muggle.javatest.JavaTestApplication.main(JavaTestApplication.java:12)
    

    哦豁!又有问题!由第一个"坑"可知,asList返回的ArrayList实际上是Arrays的内部类,而内部类ArrayList继承AbstractList抽象类,实际上,示例中的add方法调用的是抽象类AbstractList的add方法,这是一个默认实现方法

    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
        public boolean add(E e) {
            add(size(), e);
            return true;
        }
        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
    }
    

    解决的办法其实和上面一样,将asList返回的ArrayList通过new ArrayList()新建一个java.util.ArrayList对象就行了

    public class JavaTestApplication {
        public static void main(String[] args) {
            String[] strArray = {"H", "e", "l", "l", "o"};
            List stringList = new ArrayList(Arrays.asList(strArray));
            System.out.println(stringList);
            stringList.add("666");
            System.out.println(stringList);
        }
    }
    

    踩坑3.使用Arrays.asList转换基本类型对象

    如果数组是基本数据类型的呢?使用Arrays.asList会不会有问题?

    public class JavaTestApplication {
        public static void main(String[] args) {
            int[] intArray = {1,2,3,4,5,6};
            List<int[]> list = Arrays.asList(intArray);
            System.out.println(list);
            System.out.println(list.size());
            list.stream().forEach(x-> System.out.println(x.getClass()));
        }
    }
    

    输出结果:

    [[I@3feba861]
    1
    class [I
    

    从输出结果可得知,int[]数组转换为List对象后,长度只有1,类型是整型数组。因此,通过上述的使用是无法将基本数据类型的数组转换为List对象。

    有两种办法,第一种方法就是通过将基本类型数组转换为IntStream流再转换为List集合;第二种方法是将基本数据类型改为对应的包装类类型

    public class JavaTestApplication {
        public static void main(String[] args) {
            int[] intArray = {1, 2, 3, 4, 5, 6};
            // 方法1:将int数组转换为IntStream流,并且调用boxed方法将stream流内的元素都以整型存在
            List<Integer> intStreamList = Arrays.stream(intArray).boxed().collect(Collectors.toList());
            intStreamList.stream().forEach(x -> System.out.print(x + "  "));
            // 方法2:将int数组改为Integer类型
            Integer[] integerArray = {1, 2, 3, 4, 5, 6};
            List<Integer> integerList = Arrays.asList(integerArray);
            integerList.stream().forEach(x -> System.out.print(x + "  "));
        }
    }
    

    以上都是常见的Arrays使用不正确的场景,希望能够诸位在开发的时候,多加注意!

    如果觉得文章不错的话,麻烦点个赞哈,你的鼓励就是我的动力!对于文章有哪里不清楚或者有误的地方,欢迎在评论区留言~

    参考资料:

    极客时间专栏:Java业务开发常见错误100例

    相关文章

      网友评论

          本文标题:【Java 开发常见的坑】—— Arrays 使用

          本文链接:https://www.haomeiwen.com/subject/okatvktx.html