被asList坑了后,整理出Java数组与集合相互转换正确方式
[啤酒]满怀忧思,不如先干再说!做干净纯粹的技术分享!欢迎评论区或私信交流!
本文记录一下数组和集合相互转换的几种方法和一些坑,自己总结的同时也分享给有缘人,希望在工作和面试的时候有所帮助,集合和数组的作用区别就不废话了,直接进入正文!共识
首先要达成的共识:数组可以存储任意类型 的数据,包括基本数据类型和引用数据类型【也就是对象】而集合虽然可以扩展大小,但是只能存储对象并不能存储基本数据类型
所以在使用集合存储整数,字符,小数时需要使用对应的包装类
不禁问一下初学Java的小朋友,String是基本数据类型吗?[狗头][白眼]
接下来就是,数组和集合相互转换的具体实现和常见BUG的复现和解决!
数组转集合 asList基本数据类型数组问题
在 java.util.Arrays 这个JDK提供的工具类中有一个asList 方法,可以将数组转换为一个集合// 声明数组 Integer[] arr = {1,2,3,4,5}; // 转换集合 List list = Arrays.asList(arr); // 遍历数组 for (Integer arrResult : arr) { System.out.println(arrResult); } // 遍历集合 for (Integer listResult : list) { System.out.println(listResult); }
上边的数组是引用数据类型,转换时没一点问题,
但是当数组是 基本数据类型 时就会出现bug了,如下方代码,转换前数组长度为5,转换后长度变为1 // 声明数组,基本数据类型 int[] arr = {1,2,3,4,5}; // 转换集合,此时的泛型编译器自动识别为【int[]】类型 List list = Arrays.asList(arr); // 原数组长度为5 System.out.println(arr.length); // 转换后的集合大小大小为1 System.out.println(list.size());
这个问题原因通过看asList源码可以了解到 public static List asList(T... a) { return new ArrayList<>(a); }
此处发现 asList 方法接收一个泛型变长参数,而基本数据类型不能泛型化,只有对象可以泛型化,如果想让基本数据类型泛型化必须使用其包装类,如果使用基本数据类型的数组通过asList转换,则直接将数组当做一个数据塞进了集合中,所以得到的集合大小为1。
不仅仅int有这样的问题,其他的7个基本数据类型都有该问题,使用时一定注意! asList转换的集合不能添加和删除元素
下方代码将学生数组转换为集合 // 声明数组 String[] students = {"石昊","石毅","月禅","火灵儿"}; // 转换为集合 List list = Arrays.asList(students); // 向集合中添加数据 list.add("石中天");
对集合进行add或者remove方法就会出现非法操作异常
根据上边asList源码发现,是 new ArrayList 返回,问题就在于创建的ArrayList并不是java.util 包中的,而是Arrays工具类中的内部类
和 java.util.ArrayList 相同继承了java.util.AbstractList 这个抽象类,而该抽象类中的add方法默认返回异常
java.util.ArrayList:重写了add,remove等方法,所以可以添加数据 Arrays.ArrayList:并没有重写,调用时依旧可以调用,但是就会抛出异常
下方为 Arrays.ArrayList 内部类部分源码private static class ArrayList extends AbstractList 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[] 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 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 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); } }
由此看出其实asList返回的是一个长度不可变的List, 数组多长,返回的List也就多大 ,仅仅是包了一个外壳,并不支持扩容,新增数据和删除数据等操作,如果将这个List通过方法调用传递到一个要add和removeList的方法中就会产生意想不到的结果。
这可以认为是Java设计上的一个缺陷,使用时一定要注意!
如果你确定这个集合只读,而不会做修改操作,则可以放心使用,但是如果拿捏不准,就需要其装进 java.util 包下的ArrayList中,转换成 "真正的" ArrayList,以免出现不可预料的结果// 声明数组 String[] students = {"石昊","石毅","月禅","火灵儿"}; // 转换为集合 List list = Arrays.asList(students); // 放进java.util的ArrayList中 List realList = new ArrayList<>(list); // 向集合中添加数据 realList.add("石中天"); for (String s : realList) { System.out.println(s); }通过Collections.addAll转换// 声明数组 String[] students = {"石昊","石毅","月禅","火灵儿"}; // 创建集合 ArrayList list = new ArrayList<>(); Collections.addAll(list,students); list.add("石中天"); for (String s : list) { System.out.println(s); }
此处创建 java.util.ArrayList ,可以继续进行add和remove等操作,但是不能将基本数据类型的数组转换为集合通过JDK8的流操作
此处可以使用 boxed() 方法将基本数据类型的数组转换成包装类,非常方便// 声明数组 int[] arr = {1,2,3,4}; List list = Arrays.stream(arr).boxed().collect(Collectors.toList()); list.add(5); for (Integer result : list) { System.out.println(result); }集合转数组
通过集合对象的toArray方法 // 返回Object类型数组 Object[] toArray(); // 返回指定类型数组 T[] toArray(T[] a);
将Integer集合转换为Integer类型数组,这里的数组也必须是包装类 // 声明数组 List list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); Integer[] array = list.toArray(new Integer[list.size()]); for (Integer result : array) { System.out.println(result); }
这就是数组和集合之间转换的几种常见方式和隐患,尤其是asList一定不要被坑了
希望可以帮助你少走一些弯路,节约一些时间,解决一些实质问题,更多干货可持续关注[啤酒]