每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

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~报错了

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

2.2 原因分析

直接点入asList这个方法,查看源码,发现这个通过Arrays.asList创建出来的List是继承这个 AbstractList

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

分析源码发现,此时的ArrayList有 set的复用,当时并没有 add方法的复用,所以默认就是采用父类的方法了,我们再点进去父类分析

最终找到这个方法, 所以就是为什么使用Arrays.asList创建出来的list使用set,当时使用add方法就会报错

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

2.3 解决方案

//使用new ArrayList()包装一下

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

3、还有情况就是想创建一个 不可变 的List,不能set更改值,也不能add增加值,” 只读 “情况

3.1 出现情况

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

3.2 原因分析

使用 Collections.unmodifiableList方法包装List之后,重新生成的List是不能修改的,这点我们通过源码去观察一下

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解
  1. 第一步走到了这里,如果list List集合具备快速随机访问的能力(实现RandomAccess接口),then new 第一个list
  2. 否则就是new 第二个list
  3. 但是其实,第一个是继承了第二个的,所以我们直接去第二个list(UnmodifiableList中)查看一下源码

可以发现,无论是set或者是add,甚至是remove,sort,均会抛出异常

每日一记:关于Arrays.asList和Collections.unmodifiableList的一点理解

也就是说,通过这个包装方法得到的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());
    }
}

🤔上述简单模拟了一个流程,看起来能走通的亚子。如果都能确保 不会出现问题,那自然是可以的

但是问题就是,我们不能确定会不会有突发情况,例如可能会出现的问题

  1. 顾客商品传递的过程就被篡改了
  2. 商品传递没有被篡改,但是有 动了歪心思的售货员(可能想让老板快点换个车)在结算的时候, 偷偷多算了钱
  3. 当然也会存在,售货员跟顾客认识,结账的时候帮你偷偷少算点钱 ….

用代码来模拟这几种情况

  • 情况一:顾客商品传递的过程就被篡改了
/**
     * 篡改商品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/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 找到二叉树中的最大搜索二叉树(树形BP)

    给定一颗二叉树的头节点root,已知其中所有结点的值都不一样,找到含有节点最多的搜索二叉子树 我们可以每一个结点进行判断,最后得到所有结点中最大的哪一个 树形BP(递归) publ…

    Java 2023年6月7日
    072
  • 前后端分离,SpringBoot如何实现验证码操作

    验证码的功能是防止非法用户恶意去访问登录接口而设置的一个功能,今天我们就来看看在前后端分离的项目中,SpringBoot是如何提供服务的。 SpringBoot版本 本文基于的Sp…

    Java 2023年6月8日
    086
  • 分布式锁实现方案最全解读

    前言 对多线程有所了解的朋友一般都会熟悉一个概念:锁。 在多线程并发场景下,要保证在同一时刻只有一个线程可以操作某个业务、数据或者变量,通常需要使用加锁机制。比如 synchron…

    Java 2023年6月7日
    077
  • jvm:内存结构与对象内存解析

    java的跨平台性主要是因为其字节码文件可以在任何具有java虚拟机的计算机或者电子设备上运行,jvm中的字节码解析器负责将字节码文件解释成机器码运行,字节码文件.class是ja…

    Java 2023年6月7日
    078
  • 字典生成

    crunch的使用 依然是熟悉的console-crunch-xxx min:设定最小字符串长度 max:设定最大字符串长度 -o:将生成的字典保存到指定文件 -t:指定密码输出的…

    Java 2023年6月7日
    080
  • 关于为了一时方便,使用@Scheduled注解定时踩的坑

    摘要: 事情是这样的前两周在做项目的时候碰到一个需求—要求每天晚上执行一个任务,公司统一使用的是 xxl-job 写定时任务的,我当时为了方便自己,然后就简单的使用了S…

    Java 2023年6月13日
    068
  • MinIO简介和java Api的使用

    MinIO是一个对象存储服务,非常轻量,也提供了多种语言的api,可以非常方便使用。 存储桶 minio中的bucket桶 MinIO将存储空间分为多个部分,称为bucket桶,文…

    Java 2023年6月6日
    081
  • css 隐藏元素

    display , visibility 仅会改变元素显示,不会改变元素种类。可以配合 js 使用使元素可以动态的显示隐藏。 可以使用 display 或 visibility 实…

    Java 2023年6月5日
    088
  • Java排序——二分查找法

    package Array; public class array { public static void main(String[] args) { int [] array …

    Java 2023年6月8日
    095
  • java技术难点

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年5月29日
    081
  • 操作线程的方法

    操作线程的方法操作线程有很多方法,这些方法可以使线程从某一种状态过渡到另一种状态。 线程的休眠能控制线程行为的方法之一是调用sleep()方法,sleep()方法可以指定线程休眠的…

    Java 2023年6月9日
    074
  • 将springboot安装成windows服务启动。

    下载Windows Service Wrapper 本文下载了winsw-2.3.0-bin.exe。 新建一个目录aiplatformService 在目录里面新建一个aipla…

    Java 2023年5月30日
    0106
  • 快速掌握 Base 64 | Java JS 密码系列

    Java 密码系列 – Java 和 JS Base 64 Base 64 不属于密码技术,仅是编码方式。但由于在 Java、JavaScript、区块链等出现的频率较…

    Java 2023年6月16日
    076
  • 【SSM框架】SpringMVC笔记 — 汇总

    1、什么是 SpringMVC? SpringMVC 是基于 MVC 开发模式的框架,用来优化控制器。它是 Spring 家族的一员,它也具备 IOC 和 AOP。 什么是MVC?…

    Java 2023年6月8日
    080
  • 开源Winform控件库:花木兰控件库

    微信好友推荐,挺好看的Winfrom控件库,下面来看看。 介绍 基于 C#(语言) 4.0 、 VS2019 、 Net Framework 4.0(不包括Net Framewor…

    Java 2023年5月30日
    0137
  • 验证码和前台数据处理结果

    验证码和前台数据处理结果 RegistUserServlet类: @WebServlet("/registUserServlet") public class …

    Java 2023年6月6日
    066
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球