来源:juejin.cn/post/7411047482653196303
👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍; 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/; 截止目前,累计输出 71w+ 字,讲解图 3088+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2400+小伙伴加入
引言
在Java开发中,数组与集合之间的转换是一个常见的操作。Arrays.asList()
方法因其简洁的语法和便捷的使用方式,被广泛应用于将数组转换为集合(如List)。
然而,这个看似简单的方法背后却隐藏着一个大坑,如果不小心处理,可能会导致线上事故,本文也是本人自身项目遇到的惨痛经历,希望能够通过讲解这个问题,让大家在实际工作中避开这个坑。
事故回顾
这个问题是出现开发一个电商平台的订单系统时,遇到了类似的问题。需要将一个订单ID数组转换为一个List,并在后续的业务逻辑中向这个List中添加新的订单ID。
由于个人经验不足,使用了Arrays.asList()
方法,部署到线上导致在添加新订单ID时抛出了UnsupportedOperationException
异常,整个订单处理流程中断,线上系统出现了严重的故障。
影响分析
这次事故对电商平台造成了巨大的影响:
用户体验下降: 由于订单处理流程中断,用户无法正常下单,导致用户体验大幅下降。 业务中断: 订单系统的故障直接影响到了整个电商平台的业务运营,造成了大量的订单积压。 经济损失: 由于业务中断,平台失去了大量的潜在收入,给公司带来了不小的经济损失。 信任危机: 频繁的系统故障让用户对平台的信任度下降,可能导致用户流失。
后面也是及时修复,吸取本次事故教训。
事故问题描述
首先先简单描述一下这个事故问题,废话不多说,直接看实际业务代码,也就将一个数组转换为List,并对这个List进行增删操作。
Integer[] arr = {1, 2};
List<Integer> list = Arrays.asList(arr);
list.add(3);
上述代码看起来,没问题,编译器也没有报错。但是运行这段代码时,会抛出UnsupportedOperationException
异常,提示我们不支持添加操作。这究竟是怎么回事呢?
所以这是不是个大坑,如果在实际开发中没有进行自测,必定不会酿成重大线上事故。
问题分析
上述已经知道这个使用Arrays.asList()
将数组装换成List会在add情况会报错,接下来深入源码进行分析。
Arrays.asList()的内部实现
Arrays.asList(arr)
方法实际上返回的是一个Arrays类的内部类ArrayList
,而不是我们常用的java.util.ArrayList
。这个内部类ArrayList
继承自AbstractList
,并没有实现add和remove方法。
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);
}
}
可以看到并没有实现add和remove方法,这两个方法在父类中,默认抛出UnsupportedOperationException
异常。
具体原因
下面是内部源码的分析
1.内部类ArrayList的限制:
Arrays.asList(arr)
返回的内部类ArrayList是一个固定长度的列表,它没有实现add和remove方法。当我们调用 list.add(3)
时,实际上调用的是AbstractList
中的add方法,而这个方法直接抛出了UnsupportedOperationException
异常。
2.源码分析:
AbstractList的add方法如下:
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
这个方法没有具体实现,而是直接抛出异常。
对比java.util.ArrayList
类,很明显实现add和remove方法,并没有直接抛出UnsupportedOperationException
异常
解决方案
通过上述源码,为了避免这个问题,需要使用java.util.ArrayList
对Arrays.asList(arr)
返回的列表进行封装,而不是直接使用,这样就可以正常进行增删操作了,详细步骤如下;
详细步骤
在上述代码的基础上进行代码优化修改。
1.创建数组:
Integer[] arr = {1, 2};
2.转换为List:
List<Integer> list = Arrays.asList(arr);
3.封装为可变List:这个就是问题解决的关键,使用java.util.ArrayList
对Arrays.asList(arr)
进行封装
ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(arr));
4.进行增删操作:
arrayList.add(3); // 正常添加元素
arrayList.remove(1); // 正常删除元素
代码示例
最终完整代码如下:
public class Arrays_BugDemo {
public static void main(String[] args) {
Integer[] arr = {1, 2};
List<Integer> list = Arrays.asList(arr);
// 使用ArrayList进行封装
ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(arr));
try {
list.add(3); // 这里会报错
} catch (UnsupportedOperationException e) {
System.out.println("list.add(3) 报错: " + e.getMessage());
}
arrayList.add(3); // 现在可以正常添加元素
arrayList.forEach(System.out::println);
}
}
运行结果,程序可以正常执行,并且最终arrayList可以添加新元素。
总结
通过本文的详细讲解,我们可以得出以下几点结论:
Arrays.asList(arr)
返回的是一个固定长度的List集合,没有add和remove具体实现方法,不能进行增删操作,否则会报错UnsupportedException
。如果需要进行增删操作,可以使用 java.util.ArrayList
进行封装,如:new ArrayList<>(Arrays.asList(arr))
。
希望本文能够帮助大家避免类似的线上事故,提高代码的健壮性和可靠性。在实际开发中,务必注意Arrays.asList()
的使用场景,确保代码的正确性和稳定性。同时,也应该从这次事故中吸取教训,加强代码审查和测试,避免类似问题的再次发生。
👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍; 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/; 截止目前,累计输出 71w+ 字,讲解图 3088+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2400+小伙伴加入
2. 弃用 Nginx后,Pingora成为Web服务器界的新王者!
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢啦