OddFar's Notes OddFar's Notes
首页
  • Java-Se

    • Java基础
    • Java面向对象
    • Java常用类
    • Java集合框架
  • Java-Se进阶

    • JUC多线程
  • Java-ee

    • JavaWeb
  • SQL 数据库

    • MySQL
  • NoSQL 数据库

    • Redis
    • ElasticSearch
    • MongoDB
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • Spring

    • Spring
  • 中间件

    • RabbitMQ
  • Git
  • Docker
  • Jenkins
爬虫
  • Campus (opens new window)
  • 校园信息墙 (opens new window)
关于
归档
GitHub (opens new window)
首页
  • Java-Se

    • Java基础
    • Java面向对象
    • Java常用类
    • Java集合框架
  • Java-Se进阶

    • JUC多线程
  • Java-ee

    • JavaWeb
  • SQL 数据库

    • MySQL
  • NoSQL 数据库

    • Redis
    • ElasticSearch
    • MongoDB
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • Spring

    • Spring
  • 中间件

    • RabbitMQ
  • Git
  • Docker
  • Jenkins
爬虫
  • Campus (opens new window)
  • 校园信息墙 (opens new window)
关于
归档
GitHub (opens new window)
  • java-基础

  • java-面向对象

  • Java-常用类

  • Java-集合框架

    • 集合概序
    • ArrayList
    • LinkedList
    • Vevtor和Stack
      • List总结
      • HashMap
      • set
      • HashSet
      • TreeSet
      • 迭代器
      • 泛型
      • Collections工具类
    • Java-多线程

    • JavaWeb

    • Java
    • Java-集合框架
    zhiyuan
    2021-04-16
    目录

    Vevtor和Stack

    # Vevtor和Stack

    前面写了一篇关于的是LinkedList的除了它的数据结构稍微有一点复杂之外,其他的都很好理解的。这 一篇说的大家在开发中很少去用到,有的时候也可能是会用到的,了解就行。

    注意在学习这一篇之前,需要有多线程的知识:

    1)锁机制:对象锁、方法锁、类锁

    对象锁就是方法锁:就是在一个类中的方法上加上synchronized关键字,这就是给这个方法加锁了。

    类锁:锁的是整个类,当有多个线程来声明这个类的对象的时候将会被阻塞,直到拥有这个类锁的对象被销毁或者主动释放了类锁。这个时候在被阻塞住的线程被挑选出一个占有该类锁,声明该类的对象。 其他线程继续被阻塞住。例如:在类A上有关键字synchronized,那么就是给类A加了类锁,线程1第一 个声明此类的实例,则线程1拿到了该类锁,线程2在想声明类A的对象,就会被阻塞。

    2)在本文中,使用的是方法锁。

    3)每个对象只有一把锁,有线程A,线程B,还有一个集合C类,线程A操作C拿到了集合中的锁(在集合C中有用synchronized关键字修饰的),并且还没有执行完,那么线程A就不会释放锁,当轮到线程B去操作集合C中的方法时 ,发现锁被人拿走了,所以线程B只能等待那个拿到锁的线程使用完,然后才能拿到锁进行相应的操作。

    # 1、Vector概述

    image-20210331092950219

    通过API中可以知道:

    1. Vector是一个可变化长度的数组
    2. Vector增加长度通过的是capacity和capacityIncrement这两个变量,目前还不知道如何实现自动 扩增的,等会源码分析
    3. Vector也可以获得iterator和listIterator这两个迭代器,并且他们发生的是fail-fast,而不是failsafe,注意这里,不要觉得这个vector是线程安全就搞错了,具体分析在下面会说
    4. Vector是一个线程安全的类,如果使用需要线程安全就使用Vector,如果不需要,就使用arrayList
    5. Vector和ArrayList很类似,就少许的不一样,从它继承的类和实现的接口来看,跟arrayList一模一 样。

    注意:java1.5推出的java.uitl.concurrent包,为了解决复杂的并发问题的。所以开发中,不建议用vector,原因在文章的结束会有解释,需要线程安全的集合类直接用java.util.concurrent包下的类。

    # 2、Vector源码分析

    public class Vector<E>
        extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
        {
        
    	}
    
    1
    2
    3
    4
    5
    6

    我们发现Vector的继承关系和层次结构和ArrayList中的一模一样,忘记的可以去ArrayList标题查看!

    # 构造方法

    一共有四个构造方法。

    构造方法作用:

    • 初始化存储元素的容器,也就是数组,elementData,
    • 初始化capacityIncrement的大小,默认是0,这个的作用就是扩展数组的时候,增长的大小,为0 则每次扩展2倍

    Vector():空构造

    /**
     * Constructs an empty vector so that its internal data array
     * has size {@code 10} and its standard capacity increment is
     * zero.
     */
    ///看注释,这个是一个空的Vector构造方法,所以让他使用内置的数组,这里还不知道什么是内置的数组,看它调用了自身另外一个带一个参数的构造器
    public Vector() {
        this(10);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    Vector(int)

    /**
     * Constructs an empty vector with the specified initial capacity and
     * with its capacity increment equal to zero.
     *
     * @param   initialCapacity   the initial capacity of the vector
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    //注释说,给空的cector构造器用和带有一个特定初始化容量用的,并且又调用了另外一个带两个参数的构造器,并且给容量增长值(capacityIncrement=0)为0,查看vector中的变量可以发现capacityIncrement是一个成员变量
    
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    ector(int,int)

    /**
     * Constructs an empty vector with the specified initial capacity and
     * capacity increment.
     *
     * @param   initialCapacity     the initial capacity of the vector
     * @param   capacityIncrement   the amount by which the capacity is
     *                              increased when the vector overflows
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    //构建一个有特定的初始化容量和容量增长值的空的Vector,
    public Vector(int initialCapacity, int capacityIncrement) {
        super();//调用父类的构造,是个空构造
        if (initialCapacity < 0)//小于0,会报非法参数异常:不合法的容量
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];//elementData是一个成员变量数组,初始化它,并给它初始化长度。默认就是10,除非自己给值。
        this.capacityIncrement = capacityIncrement;//capacityIncrement的意思是如果要扩增数组,每次增长该值,如果该值为0,那数组就变为两倍的原长度,这个之后会分析到
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    Vector(Collection c)

    /**
     * Constructs a vector containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this
     *       vector
     * @throws NullPointerException if the specified collection is null
     * @since   1.2
     */
    //将集合c变为Vector,返回Vector的迭代器。
    public Vector(Collection<? extends E> c) {
        elementData = c.toArray();
        elementCount = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 核心方法

    add()方法

    /**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    
    //就是在vector中的末尾追加元素。但是看方法,synchronized,明白了为什么vector是线程安全的,因为在方法前面加了synchronized关键字,给该方法加锁了,哪个线程先调用它,其它线程就得等着,如果不清楚的就去看看多线程的知识,到后面我也会一一总结的。
    public synchronized boolean add(E e) {
        modCount++;
        //通过arrayList的源码分析经验,这个方法应该是在增加元素前,检查容量是否够用
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    ensureCapacityHelper(int)

    /**
     * This implements the unsynchronized semantics of ensureCapacity.
     * Synchronized methods in this class can internally call this
     * method for ensuring capacity without incurring the cost of an
     * extra synchronization.
     *
     * @see #ensureCapacity(int)
     */
    ////这里注释解释,这个方法是异步(也就是能被多个线程同时访问)的,原因是为了让同步方法都能调用到这个检测容量的方法,比如add的同时,另一个线程调用了add的重载方法,那么两个都需要同时查询容量够不够,所以这个就不需要用synchronized修饰了。因为不会发生线程不安全的问题
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            //容量不够,就扩增,核心方法
            grow(minCapacity);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    grow(int)

    //看一下这个方法,其实跟arrayList一样,唯一的不同就是在扩增数组的方式不一样,如果capacityIncrement不为0,那么增长的长度就是capacityIncrement,如果为0,那么扩增为2倍的原容量
        
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    只要能看的懂ArrayList,这个就是在每个方法上比arrayList多了一个synchronized,其他都一 样。这里就不再分析了!

    public synchronized E get(int index) {
        if (index >= elementCount)
        	throw new ArrayIndexOutOfBoundsException(index);
        
        return elementData(index);
    }
    
    1
    2
    3
    4
    5
    6

    # 3、Stack

    现在来看看Vector的子类Stack,学过数据结构都知道,这个就是栈的意思。那么该类就是跟栈的用法一 样了

    class Stack<E> extends Vector<E> {}
    
    1

    通过查看他的方法,和查看api文档,很容易就能知道他的特性。就几个操作,出栈,入栈等,构造方法也是空的,用的还是数组,父类中的构造,跟父类一样的扩增方式,并且它的方法也是同步的,所以也是线程安全。

    image-20210331133023382

    # 4、总结Vector和Stack

    【Vector总结(通过源码分析)】

    1. Vector线程安全是因为它的方法都加了synchronized关键字
    2. Vector的本质是一个数组,特点能是能够自动扩增,扩增的方式跟capacityIncrement的值有关
    3. 它也会fail-fast,还有一个fail-safe两个的区别在下面的list总结中会讲到。

    【Stack的总结】

    1. 对栈的一些操作,先进后出
    2. 底层也是用数组实现的,因为继承了Vector
    3. 也是线程安全的
    在 GitHub 上编辑此页 (opens new window)
    最后更新: 2023/03/26, 13:03:00
    LinkedList
    List总结

    ← LinkedList List总结→

    Theme by Vdoing | Copyright © 2021-2023 oddfar | 冀ICP备20001094号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式