zoukankan      html  css  js  c++  java
  • java源码--Vector和Stack

    一、Vector简介

    1.1、Vector概述

    通过API中可以知道:

        1)Vector是一个可变化长度的数组
        2)Vector增加长度通过的是capacity和capacityIncrement这两个变量
        3)Vector也可以获得iterator和listIterator这两个迭代器,并且他们发生的是fail-fast,而不是fail-safe,注意这里,不要觉得这个vector是线程安全就搞错了
        4)Vector是一个线程安全的类,如果使用需要线程安全就使用Vector,如果不需要,就使用arrayList
        5)Vector和ArrayList很类似,就少许的不一样,从它继承的类和实现的接口来看,跟arrayList一模一样。只是一些细节上有区别:比如线程安全与否,扩容的大小等,Vector的线程安全通过在方法上直接加synchronized实现。扩容默认扩大为原来的2倍。

    2.2、源码分析

      2.2.1 属性

    //底层结构为object数组,和ArrayList一样,不过我们可以注意到访问修饰符有所不同,Vector用protected修饰,
    //而ArrayList用private修饰。我们知道private变量只能被当前类的方法访问,而protected可以被同一包中的所有类和其他包的子类访问
    //这就是存储数据的数组,注意啦这是一个动态的数组
    protected Object[] elementData;
    //当前元素的个数
    protected int elementCount;
    //容量增长系数,扩容时使用
    protected int capacityIncrement;
    // Vector的序列版本号 private static final long serialVersionUID = -2767605614048989439L;

       2.2.2 构造方法

    // 默认构造函数
    Vector()
    // capacity是Vector的默认容量大小。当由于增加数据导致容量增加时,每次容量会增加一倍。
    Vector(int capacity)
    // capacity是Vector的默认容量大小,capacityIncrement是每次Vector容量增加时的增量值。
    Vector(int capacity, int capacityIncrement)
    // 创建一个包含collection的Vector
    Vector(Collection<? extends E> collection)
    1)Vector():空构造
    //这个是一个空的Vector构造方法,所以让他使用内置的数组,这里还不知道什么是内置的数组,看它调用了自身另外一个带一个参数的构造器
        public Vector() { this(10); }
    2)Vector(int)
    //给空的cector构造器用和带有一个特定初始化容量用的,并且又调用了另外一个带两个参数的构造器,并且给容量增长值(capacityIncrement=0)为0,查看vector中的变量可以发现capacityIncrement是一个成员变量
        public Vector(int initialCapacity) {
            this(initialCapacity, 0);
        }
    3)Vector(intint)
    //构建一个有特定的初始化容量和容量增长值的空的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,那数组就变为两倍的原长度
        }
    4)Vector(Collection<? extends E> c)
    //将集合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);
        }

       2.2.2 核心方法--扩容

    add()方法
    //就是在vector中的末尾追加元素。但是看方法,synchronized,明白了为什么vector是线程安全的,因为在方法前面加了synchronized关键字,给该方法加锁了,哪个线程先调用它,其它线程就得等着
        public synchronized boolean add(E e) {
            modCount++;
    //通过arrayList的源码分析经验,这个方法应该是在增加元素前,检查容量是否够用
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = e;
            return true;
        }
    ensureCapacityHelper(int)
    //这里注释解释,这个方法是异步(也就是能被多个线程同时访问)的,原因是为了让同步方法都能调用到这个检测容量的方法,比如add的同时,另一个线程调用了add的重载方法,那么两个都需要同时查询容量够不够,所以这个就不需要用synchronized修饰了。因为不会发生线程不安全的问题
        private void ensureCapacityHelper(int minCapacity) {
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
    //容量不够,就扩增,核心方法
                grow(minCapacity);
        }
    grow(int) 扩容核心函数
    //看一下这个方法,其实跟arrayList一样,唯一的不同就是在扩增数组的方式不一样,如果capacityIncrement不为0,那么增长的长度就是capacityIncrement,如果为0,那么扩增为2倍的原容量
        private void grow(int minCapacity) {
             // overflow-conscious code
         //将旧的容量赋值为数组长度
    int oldCapacity = elementData.length;
         //如果容量增量系数>0那么设置新容量为oldCapacity+capacityIncrement,否则为oldCapacity + oldCapacity
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
         //如果新容量<数组实际所需容量,令newCapacity = minCapacity
    if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
         //如果新容量大于数组最大扩容大小,如果当前所需容量>MAX_ARRAY_SIZE,那么新容量设为 Integer.MAX_VALUE,否则设为 MAX_ARRAY_SIZE if (newCapacity - MAX_ARRAY_SIZE > 0)
    if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }

      2.2.3 其他方法

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

    二、Stack

        

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

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

      

    三、总结

      3.1 VectorStack

    Vector
    1)Vector线程安全是因为其中的方法都使用了synchronized
    2)Vector的本质是一个数组,特点能是能够自动扩增,扩增的方式跟capacityIncrement的值有关
    3)它也会fail-fast,还有一个fail-safe
    Stack
    1)对栈的一些操作,先进后出
    2)底层也是用数组实现的,因为继承了Vector
    3)也是线程安全的

      3.2 ArrayListLinkedList

    arrayList底层是用数组实现的顺序表,是随机存取类型,可自动扩增,并且在初始化时,数组的长度是0,只有在增加元素时,长度才会增加。默认是10,不能无限扩增,有上限,在查询操作的时候性能更好
    
    LinkedList底层是用链表来实现的,是一个双向链表,注意这里不是双向循环链表,顺序存取类型。在源码中,似乎没有元素个数的限制。应该能无限增加下去,直到内存满了在进行删除,增加操作时性能更好。
    
    两个都是线程不安全的,在iterator时,会发生fail-fast。

      3.3 fail-fastfail-safe

    简单的来说:在java.util下的集合都是发生fail-fast,而在java.util.concurrent下的发生的都是fail-safe。
    
      1)fail-fast
    
        快速失败,例如在arrayList中使用迭代器遍历时,有另外的线程对arrayList的存储数组进行了改变,比如add、delete、等使之发生了结构上的改变,
    
        所以Iterator就会快速报一个java.util.ConcurrentModificationException 异常(并发修改异常),这就是快速失败。
    
      2)fail-safe
    
        安全失败,在java.util.concurrent下的类,都是线程安全的类,他们在迭代的过程中,如果有线程进行结构的改变,不会报异常,而是正常遍历,这就是安全失败。
    
      3)为什么在java.util.concurrent包下对集合有结构的改变,却不会报异常?
    
        在concurrent下的集合类增加元素的时候使用Arrays.copyOf()来拷贝副本,在副本上增加元素,如果有其他线程在此改变了集合的结构,那也是在副本上的改变,而不是影响到原集合,
    
        迭代器还是照常遍历,遍历完之后,改变原引用指向副本,所以总的一句话就是如果在此包下的类进行增加删除,就会出现一个副本。所以能防止fail-fast,这种机制并不会出错,所以我们叫这种现象为fail-safe。   
    
      4)vector也是线程安全的,为什么是fail-fast呢?
    
        这里搞清楚一个问题,并不是说线程安全的集合就不会报fail-fast,而是报fail-safe,你得搞清楚前面所说答案的原理,出现fail-safe是因为他们在实现增删的底层机制不一样,就像上面说的,
    
        会有一个副本,而像arrayList、linekdList、verctor等,他们底层就是对着真正的引用进行操作,所以才会发生异常。
    
      5)既然是线程安全的,为什么在迭代的时候,还会有别的线程来改变其集合的结构呢(也就是对其删除和增加等操作)?
    
        首先,我们迭代的时候,根本就没用到集合中的删除、增加,查询的操作,就拿vector来说,我们都没有用那些加锁的方法,
    
        也就是方法锁放在那没人拿,在迭代的过程中,有人拿了那把锁,我们也没有办法,因为那把锁就放在那边。
  • 相关阅读:
    Oracle数据库的左连接和右连接(+)
    Web文件上传模块 Plupload
    增加反向链接的35个技巧
    google map api 与jquery结合使用(1)控件,监听器[转帖]
    教你在windows 7/xp 下安装使用mencoder
    Oracle 全文索引
    提高关键词排名的28个SEO技巧
    二叉树遍历及C语言实现
    小额担保业务管理系统详细设计介绍
    C#与数据结构二叉树的遍历
  • 原文地址:https://www.cnblogs.com/FondWang/p/11772331.html
Copyright © 2011-2022 走看看