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