• java 容器


    java 容器

    数组

    数组的扩容问题

    ArrayList 的默认初始化容量为0,首次添加元素时,创建容量为(10 || 添加集合大小) ,以后每次扩容的话,为当前容量的1.5倍

    public ArrayList() {
        /*
        初始化容量大小为0
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        */
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
     private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                //容量取 10 或 minCapacity
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
    
    private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
        //扩容机制,向右移动1位 即1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    并发访问数组问题

    当我们遍历的同时修改数组元素的时候我们会面临并发修改数组的问题,比如我们使用 iterator 迭代器进行数组的遍历,同时使用 arrayList.remove进行元素的删除,则会导致并发修改异常。

    导致并发修改的问题相当于文件快照版本号的对比。

    private class Itr implements Iterator<E> {
    		//在创建迭代器的时候会记录快照的版本号
            int expectedModCount = modCount;
    
            Itr() {}
    }
    //每次在next 或者 remove 的时候会进行快照的检查如果快照版本已经被修改则会导致 并发修改异常
    public E next() {
        //检查版本号是否匹配
        checkForComodification();
    }
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    解决方案:

    使用 iterator 提供的 remove 方法,或使用 for() 从尾部进行迭代并删除(避免头部操作的话导致的数组越界异常

    HashMap

    ConcurrentHashMap

    CopyOnWriteArrayList

    从两个方面入手分析,分别从 add 方法,代表 操作数组的所有写的方法,二是从iterator方法,代表所有读的方法

     public boolean add(E e) {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                Object[] elements = getArray();
                int len = elements.length;
                //我们这里看到每次进行写入的时候我们都会进行加锁操作并且创建一个新的数组替换原有的数组
                Object[] newElements = Arrays.copyOf(elements, len + 1);
                newElements[len] = e;
                setArray(newElements);
                return true;
            } finally {
                lock.unlock();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    public Iterator<E> iterator() {
        //创建iterator 并将数组的引用传递给iterator ,由于没有加锁,现在我们知道,我们读取的数据,并不一定是实时可见的
        return new COWIterator<E>(getArray(), 0);
    }
    
    • 1
    • 2
    • 3
    • 4

    Queue

    queue 的继承关系图

    queue 接口提供的方法

    Summary of Queue methods
    Throws exceptionReturns special value
    Insert Queue#add add(e) Queue#offer offer(e)
    Remove Queue#remove remove() Queue#poll poll()
    Examine 查看但不进行元素删除的操作 Queue#element element() Queue#peek peek()

    Deque 接口提供的方法

    Summary of Deque methods
    First Element (Head) Last Element (Tail)
    Throws exceptionSpecial valueThrows exceptionSpecial value
    Insert Deque#addFirst addFirst(e) Deque#offerFirst offerFirst(e) Deque#addLast addLast(e) Deque#offerLast offerLast(e)
    Remove Deque#removeFirst removeFirst() Deque#pollFirst pollFirst() Deque#removeLast removeLast() Deque#pollLast pollLast()
    Examine Deque#getFirst getFirst() Deque#peekFirst peekFirst() Deque#getLast getLast() Deque#peekLast peekLast()

    BlockingQueue接口提供的方法

    Summary of BlockingQueue methods
    Throws exceptionSpecial valueBlocksTimes out
    Insert #add add(e) #offer offer(e) #put put(e) #offer(Object, long, TimeUnit) offer(e, time, unit)
    Remove #remove remove() #poll poll() #take take() #poll(long, TimeUnit) poll(time, unit)
    Examine #element element() #peek peek()not applicablenot applicable

    其他综合说明

    ConcurrentLinkedQueue 是无限容量,LinkedBlockingQueue可以指定容量,在不指定容量时,默认容量为Integer 的最大值, ArrayBlockingQueue 需要指定容量,SynchronousQueue 容量为1,进行同步交换。

    关于线程池还有一点说明,线程池中将command 从 blockingQueue 中取出时,并非用的 put 方法,而是使用的offer() 非阻塞方法。

    //固定大小线程池使用的是  容量为interger 的最大值的  queue
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),
                                          threadFactory);
    }
    //单线程池使用的是  容量为interger 的最大值的  queue
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    // 缓存线程池使用的是 同步的队列
    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
    }
    //使用的是DelayedWorkQueue
    public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                  new DelayedWorkQueue());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    学习资料推广

    我已经将springamqp 源码解析录制为视频上传到bibi,分为六个章节详细介绍了各个模块的具体内容

    https://www.bilibili.com/video/BV1hN411Z7fn?share_source=copy_web

    感兴趣的小伙伴可以看看
    学习一下
    录制不易,记得三联哦!

  • 相关阅读:
    Gitlab和SonarQube的安装、集成、使用(Docker、GitFlow、IDEA)
    Spring Boot + Vue的网上商城之springsecurity+jwt+redis实现用户权限认证实现
    基于ffmpeg给视频添加时间字幕
    Jenkins如何安装配置Allure插件及工具
    如何做好分支管理,保证高效CI/CD?
    Java中如何删除List集合中的元素呢?
    Linux 多线程多进程
    橘子学Flink03之Flink的流处理与批处理
    MobPush HarmonyOS NEXT 版本集成指南
    单元测试导包
  • 原文地址:https://blog.csdn.net/c1523456/article/details/127954496