集合容器-CopyOnWriteArrayList(即数组小练习)

CopyOnWriteArrayList实现类,

Java并发包中的并发list,CopyOnWriteArrayList是一个ArrayList的一个线程安全的变体,写数组的拷贝,

对其进行修改操作都是底层 的一个复制数组中进行的,也就是使用写数据时复制策略

写时复制(CopyOnWrite,简称cow)思想计算机涉及领域中的一种优化策略,其核心思想是多个调用者(Callers),同时要求同一个资源(内存,磁盘上的数据存储,cpu),调用者们会获取相同的指针指向相同的资源,直到某一个调用者试图修改资源的内容时,系统才会真正复制一份专用的副本给调用者,

测试

//多线程下迭代器的弱一致性结果
public class MethodGeneric {
    //如果多个线程同时操作volatile变量,那么对该变量的写操作必须在读操作之前执行(禁止重排序),并且写操作的结果对读操作可见(强缓存一致性)。
    private static volatile CopyOnWriteArrayList<String > copyOnWriteArrayList = new CopyOnWriteArrayList<String>();
    public static void main(String[] args) {
        copyOnWriteArrayList.add("hello");
        copyOnWriteArrayList.add("word");
        copyOnWriteArrayList.add("welcome");
        copyOnWriteArrayList.add("to");
        copyOnWriteArrayList.add("xi`an");
        System.out.println("原有数据"+copyOnWriteArrayList);

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                //修改list结合中索引为4的元素
                copyOnWriteArrayList.set(4,"sx");
                //删除元素
                copyOnWriteArrayList.remove(1);
                System.out.println("线程copy的数组"+copyOnWriteArrayList);

            }
        });

        copyOnWriteArrayList.set(4,"sx");
        //在保证修改线程之前获取迭代器
        Iterator<String> iterator = copyOnWriteArrayList.iterator();
        //启动线程
        thread.start();
        try {
            // 等待线程执行完毕
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //迭代器遍历元素
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("修改后的数据"+copyOnWriteArrayList);

    }
}

测试结果

在这里插入图片描述

输出后的结果,线程操作没有使原有数据进行修改操作,

分析源码

CopyOnWriteArrayList添加方法

添加前先拷贝原有数组到新数组中。再将元素赋值到新数组的末端。然后将新数组复制到原来数组中,保证原有数组的安全性

public boolean add(E e) {
    //lock变量被final修饰,赋值后不可被修改,增加了变量的安全性
    final ReentrantLock lock = this.lock;
    
    //获取锁
    lock.lock();
    try {
        //获取原有数组
        Object[] elements = getArray();
        
        //数组长度赋值到len变量中
        int len = elements.length;
        
        //创建新数组,将原来的数组的元素,长度加1拷贝到新数组中,在新数组中操作避免了直接操作原有数组
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        
        //将参数中传递的e赋值到newElemnts[]数组的末端
        newElements[len] = e;
        
        //最终将数组复制到原来数组中
        setArray(newElements);
        
        return true;
    } finally {
        //释放锁
        lock.unlock();
    }
}

获取数组

//最终方法,此方法不能被重写
final Object[] getArray() {
    //返回数组
    return array;
}

设置数组

//最终方法,
final void setArray(Object[] a) {
    //数组的赋值
    array = a;
}

数组存储

//从源码中可以看出,该方法只能被setArray/getArray方法访问,
/**
 * transient : 说明该数组不参与序列化,和反序列化操作,
 * volatile : jvm提供的轻量级的同步机制,保证数组的可见性,无原子性,指令禁重排
 */
private transient volatile Object[] array;

CopyOnWriteArrayList修改方法

替换数组中指定位置的元素。

public E set(int index, E element) {
    
    //lock变量被final修饰,赋值后不可被修改,增加了变量的安全性
    final ReentrantLock lock = this.lock;
    
    //获取锁
    lock.lock();
    try {
        //获取原有数组
        Object[] elements = getArray();
        
        //获取要修改的数组以索引
        E oldValue = get(elements, index);
        
		/*
		 * 判断要修改的元素与数组中的是否一致
		 * 一致走else语句重新赋值给array
		 * 不一致则走if语句
		 */
        if (oldValue != element) {
            
            //数组长度赋值到len变量
            int len = elements.length;
            
            //将原有数组即长度拷贝到新数组中,在新数组中操作避免了直接操作原有数组
            Object[] newElements = Arrays.copyOf(elements, len);
            
            //对新数组的索引位置进行赋值
            newElements[index] = element;
            
            //最终将数组复制到原来数组中
            setArray(newElements);
        } else {
            // 不做任何操作,重新赋值到原来数组中
            setArray(elements);
        }
        return oldValue;
    } finally {
        //释放锁
        lock.unlock();
    }
}

获取指定索引

private E get(Object[] a, int index) {
    return (E) a[index];
}

附加数组小练习

package com.sin.demo.test.demo;

import java.util.Arrays;
import java.util.Scanner;

/**
 * 标题:ArraysDataBase
 * 题目内容:在查找的时候可以直接访问原数组,在做改动(添加元素,修改元素,删除元素)先拷贝一份源数组,
 * 再在拷贝的数组中进行操作(添加元素,修改元素,删除元素),最后是否覆盖源数据(类似数据库的提交事务:commit)
 *
 * @author sin
 * @date 2022/10/14
 * @apiNote
 */
public class ArraysDataBaseDemo {



    public static void main(String[] args) {
        //从控制台中扫描数据
        Scanner scanner = new Scanner(System.in);

        byte num = 0;//定义变量,给谁使用do—while()使用的
        //创建新数组
        String[] newUserData;
        //定义原数据
        String[] userData = new String[]{"张三、18、123456", "李四、23、123456", "王五、36、123456",
                "赵六、32、321321", "唐七、46、123123"};

//        //先计算原数组长度,5赋值,
//        int userDataLength = userData.length;//5

        do {

            //循环计算源数组的长度
            int userDataLength = userData.length;


            System.out.println("--------------------------------ArraysDataBase-----------------------------------");

            System.out.println("【1】查看数据");
            System.out.println("【2】增加数据");
            System.out.println("【3】删除数据");
            System.out.println("【4】修改数据");
            System.out.println("【5】退出系统");

            //选择功能
            num = scanner.nextByte();

            //1,查看数据-------------------------------------------------------------------------------------------------
            //检测控制台中输入的为1时进入if语句
            if (num == 1) {
                System.out.println("【1】查看数据");

                System.out.println("id 姓名 年龄  密码");
                //循环遍历userData(原数组)数组的每一个元素
                for (int i = 0; i < userDataLength; i++) {
                    System.out.println((i + 1) + "  " + userData[i]);
                }
            }

            //2,增加数据-------------------------------------------------------------------------------------------------
            //检测控制中输入的为2时进入if语句
            if (num == 2) {
                System.out.println("【2】增加数据");
                //创建新数组newUserData
                newUserData = new String[userDataLength + 1];

                //将newUserData数组的长度赋值给newUserDataLength变量中
                int newUserDataLength = newUserData.length;

                //循环将userData(原数组)的元素依次赋值给newUserData(新数组)中
                //如果使用userData(原数组)的的length(长度)会导致下标越界异常出错,因为原数组的长度要比新数组的长度少一个元素位置
                for (int i = 0; i < userDataLength; i++) {
                    newUserData[i] = userData[i];
                }

                System.out.println("id 姓名 年龄  密码");
                //循环遍历复制过后的(newUserData)新数组元素
                for (int i = 0; i < newUserDataLength - 1; i++) {
                    System.out.println((i + 1) + "  " + newUserData[i]);
                }

                System.out.println("请输入要增加的数据");
                String insertData = scanner.next();
                //给newUserData数组中的最后一个元素赋值
                newUserData[newUserDataLength - 1] = insertData;


                System.out.println("id 姓名 年龄 密码");
                //循环遍历添加元素后的newUserData数组
                for (int i = 0; i < newUserDataLength; i++) {
                    System.out.println((i + 1) + "  " + newUserData[i]);
                }

                System.out.println("是否提交数据");
                String commitData = scanner.next();
                if (commitData.equals("提交")) {
                    //将(newUserData)新数组的所有元素以及长度复制给(userData)原数组
                    //userData = newUserData;

                    /**
                     * Arrays : 操作数组用的类
                     * copyOf : 拷贝数组方法
                     *      newUserData : 被拷贝的数组
                     *      newUserDataLength :将要拷贝的数组长度
                     * userData : 将要拷贝的数组
                     */
                    userData = Arrays.copyOf(newUserData, newUserDataLength);
                    System.out.println("提交成功");
                } else {
                    System.out.println("提交失败");
                }


            }

            //3,删除数据-------------------------------------------------------------------------------------------------
            if (num == 3) {
                System.out.println("【3】删除数据");

                //把userData数组中的长度及元素复制给newUserData;
                newUserData = userData;

                int newUserDataLength = newUserData.length;

                System.out.println("id 姓名 年龄 密码");
                //遍历userData数组中的元素
                for (int i = 0; i < newUserDataLength; i++) {
                    System.out.println((i + 1) + "  " + userData[i]);
                }

                System.out.println("请输入要删除的id");
                //从控制台中获取要删除的变量值,减1:元素对应的个数(索引时从0开始的)
                int deleteDataById = (scanner.nextInt() - 1 ) ;//输入的数字 跟要删除的索引 是同一条元素


                //如果输入的长度不超过userData数组的长度则进入if语句
                if (deleteDataById != 0 && deleteDataById < newUserDataLength) {

                    userData[deleteDataById] = null;//输入具体的索引并将其赋null值
                    //从删除的索引开始遍历元素
                    for (int i = deleteDataById; i < newUserDataLength - 1; i++) {
                        newUserData[i] = newUserData[i + 1];//将i索引往后的索引往前索引赋值
                    }
                    newUserData[newUserDataLength - 1] = null;//将最后一个索引位置的元素赋null值

                    System.out.println("删除成功");

                    System.out.println("id 姓名 年龄 密码");
                    //循环遍历newUserDataLength数组的元素
                    for (int i = 0; i < newUserDataLength; i++) {
                        System.out.println((i + 1) + "  " + newUserData[i]);
                    }

                    System.out.println("是否提交数据");
                    String commitData = scanner.next();
                    if (commitData.equals("提交")) {
                        //把newUserData数组copy给userData数组
                        userData = Arrays.copyOf(newUserData, newUserDataLength - 1);
                        System.out.println("提交成功");
                    } else {
                        System.out.println("提交失败");
                    }

                } else {
                    System.out.println("没有id为:【" + (deleteDataById + 1) + "】数据");
                }

            }

            //4,修改数据-------------------------------------------------------------------------------------------------
            if (num == 4) {
                System.out.println("【4】修改数据");
                //将userData数组的元素及长度,一并复制给newUserData
                newUserData = userData ;

                //将newUserData数组的长度赋值给变量
                int newUserDataLength = newUserData.length;

                System.out.println("id 姓名 年龄 密码");
                //循环遍历newUserDataLength数组的元素
                for (int i = 0; i < newUserDataLength; i++) {
                    System.out.println((i + 1) + "  " + newUserData[i]);
                }

                System.out.println("请输入要修改的id");
                int updateData = scanner.nextInt();

                System.out.println("请输入您要修改【"+updateData+"】数据的内容");
                String update = scanner.next();
                newUserData[updateData - 1] = update;

                System.out.println("修改成功");

                System.out.println("id 姓名 年龄 密码");
                //循环遍历newUserDataLength数组的元素
                for (int i = 0; i < newUserDataLength; i++) {
                    System.out.println((i + 1) + "  " + newUserData[i]);
                }

                System.out.println("是否提交数据");
                String commitData = scanner.next();
                if (commitData.equals("提交")) {
                    //把newUserData数组copy给userData数组
                    userData = Arrays.copyOf(newUserData, newUserDataLength);
                    System.out.println("提交成功");
                } else {
                    System.out.println("提交失败");
                }

            }

            //5,退出程序-------------------------------------------------------------------------------------------------
            if (num == 5) {//如果输入5直接退出该方法结束程序
                return;
            }
        } while (num != 0);

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆卿之

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值