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

日常开发中,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例

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容