1、正常创建一个List,对List进行操作
List collect = Stream.of(1 ,3 ,5 ,7 ,9).collect(Collectors.toList());
//第一位改变为9
collect.set(0, 9);
//尾部插入一个值
collect.add(99);
collect.forEach(System.out::println);
//output
9
3
5
7
9
99
2、有时候为了更加方便的创建List,使用Arrays.asList
2.1 出现情况
List asList = Arrays.asList(1 ,3 ,5 ,7 ,9);
asList.set(0,9);
//asList.add(99);
asList.forEach(System.out::println);
//output
9
3
5
7
9
//把上述注释放开
oops~报错了
2.2 原因分析
直接点入asList这个方法,查看源码,发现这个通过Arrays.asList创建出来的List是继承这个 AbstractList的
分析源码发现,此时的ArrayList有 set的复用,当时并没有 add方法的复用,所以默认就是采用父类的方法了,我们再点进去父类分析
最终找到这个方法, 所以就是为什么使用Arrays.asList创建出来的list使用set,当时使用add方法就会报错
2.3 解决方案
//使用new ArrayList()包装一下
3、还有情况就是想创建一个 不可变 的List,不能set更改值,也不能add增加值,” 只读 “情况
3.1 出现情况
3.2 原因分析
使用 Collections.unmodifiableList方法包装List之后,重新生成的List是不能修改的,这点我们通过源码去观察一下
- 第一步走到了这里,如果list List集合具备快速随机访问的能力(实现RandomAccess接口),then new 第一个list
- 否则就是new 第二个list
- 但是其实,第一个是继承了第二个的,所以我们直接去第二个list(UnmodifiableList中)查看一下源码
可以发现,无论是set或者是add,甚至是remove,sort,均会抛出异常
也就是说,通过这个包装方法得到的list集合,是只读的,不可变的,正如方法名所说的一样
3.3 适用场景
模拟一个大型购物商场,顾客选择完自己想要购买的商品,到售货员哪里进行结算的流程
抽象出来两个角色:顾客和售货员
/**
* 模拟顾客购买商品的流程
*
* @author Amg
* @date 2021/9/8 10:01
*/
public class Customer {
private List ids;
Customer() {
ids = new ArrayList<>();
}
/**
* 添加商品
* @param goodsId 商品主键id
*/
public void add(Long goodsId) {
ids.add(goodsId);
}
/**
* 移除商品
* @param goodsId 商品主键id
*/
public void remove(Long goodsId) {
ids.remove(goodsId);
}
/**
* 返回最终的商品列表
* @return List
*/
public List getIds() {
return ids;
}
}
/**
* 售货员
* @author Amg
* @date 2021/9/8 10:18
*/
public class Sales {
private List list;
public Sales(List goodsId) {
list = goodsId;
}
/**
* 结算
*/
public double countPrice() {
double price = 0;
for (Long id : list) {
//根据list里面的商品获取价格,这里简单先模拟
if (id % 2 == 0) {
price += 1;
} else {
price += 2;
}
}
return price;
}
}
/**
* 商场客户端
* @author Amg
* @date 2021/9/8 10:17
*/
public class Client {
public static void main(String[] args) {
Customer customer = new Customer();
//添加商品
customer.add(123L);
customer.add(456L);
customer.add(789L);
//移除商品
customer.remove(123L);
List ids = customer.getIds();
Sales sales = new Sales(ids);
System.out.println(sales.countPrice());
}
}
🤔上述简单模拟了一个流程,看起来能走通的亚子。如果都能确保 不会出现问题,那自然是可以的
但是问题就是,我们不能确定会不会有突发情况,例如可能会出现的问题
- 顾客商品传递的过程就被篡改了
- 商品传递没有被篡改,但是有 动了歪心思的售货员(可能想让老板快点换个车)在结算的时候, 偷偷多算了钱
- 当然也会存在,售货员跟顾客认识,结账的时候帮你偷偷少算点钱 ….
用代码来模拟这几种情况
- 情况一:顾客商品传递的过程就被篡改了
/**
* 篡改商品id
* @param goodsId 旧的商品列表
* @return 返回一个新的篡改好的list
*/
public static void tamperOldIds(List goodsId) {
//在这里进行篡改,添加或者删除,这里模拟添加
goodsId.add(999L);
goodsId.add(1097L);
goodsId.add(573L);
}
//Client调用的时候
//假设被替换了
tamperOldIds(ids);
Sales sales = new Sales(ids);
System.out.println(sales.countPrice());
//output就是篡改后的计算的价格
- 情况二:被售货员篡改了(多算/少算 都是篡改)
public double countPrice() {
double price = 0;
//动了歪心思,售货员篡改商品,这里也是模拟添加
list.add(3332L);
list.add(3336L);
list.add(3339L);
for (Long id : list) {
//根据list里面的商品获取价格,这里简单先模拟
if (id % 2 == 0) {
price += 1;
} else {
price += 2;
}
}
return price;
}
所以可以得到一个结论, 我们要杜绝顾客的购买商品列表被篡改
使用 Collections.unmodifiableList 来包装一下顾客的购买商品列表,即可使其不能再被修改,上述的修改就都行不通了
/**
* 返回最终的商品列表,只读
* @return List
*/
public List getIds() {
return Collections.unmodifiableList(ids);
}
所以Collections.unmodifiableList方法适用的场景就是 某一份数据在获取的时候就不能再被修改
记住一个词就好了, 只读
Original: https://www.cnblogs.com/iamamg97/p/15244155.html
Author: 码农Amg
Title: 每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570339/
转载文章受原作者版权保护。转载请注明原作者出处!