Java 集合深入理解 :线程安全的数组集合(Vector)

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 集合深入理解 :线程安全的数组集合(Vector)相关的知识,希望对你有一定的参考价值。

Java 集合深入理解 (三) :java.util 包的集合中 fail-fast 快速失败机制

Java 集合深入理解 (一): ArrayList

简介

Vector的字面意思是矢量; 向量;可以用来代替数组,主要不同是vector的长度可以自动增长,那既然是这样,和arraylist有什么区别,主要区别还是在 该应用是java的应用是安全指针!
并是java集合框架在现在已经在官方文档并不推荐使用了,和arraylist一样同样维护一个数组, 但通过加synchronized来保证线程安全,有自己的扩容方式

关键方法时间复杂度

  • get() 直接读取第几个下标,复杂度 O(1)
  • add(E) 添加元素,直接在后面添加,复杂度O(1)
  • add(index, E) 添加元素,在第几个元素后面插入,后面的元素需要向后移动,复杂度O(n)
  • remove()删除元素,后面的元素需要逐个移动,复杂度O(n)

示例

	public static void main(String[] args) {
		
		System.out.println("--------------Vector--------------");
		Vector v=new Vector();
		v.add(1);
		v.add(2);
		v.add(3);
		v.add(7);
		v.add(5);
		v.add(6);
		
		v.stream().forEach(m->{
			System.out.println(m);
		});
	}
--------------Vector--------------
1
2
3
7
5
6

从上面的示例可以看出

  • 我们存储的数据是顺序的,遍历出的数据也是按照插入数据取出的
  • 虽然我没有写多线程,但是add方法是synchronized修饰的,所以在多线程的情况下数据修改是安全的

全篇注释

/**
*{@code Vector}类实现了一个可增长的对象。与数组一样,它包含可以使用整数索引访问。然而,一个
*{@code Vector}可以根据需要增长或收缩以适应在创建{@code Vector}之后添加和删除项。<p>每个向量都试图通过维护
*{@code capacity}和{@code capacityIncrement}。这个{@code capacity}总是至少和向量一样大尺寸;它通常更大,因为组件添加到
*向量,向量的存储以块的大小增加{@code capacityIncrement}。应用程序可以提高
*在插入大量数据之前向量的容量部件;这减少了增量再分配的数量。
*<p><a name=“fail fast”>java中的fail-fast(快速失败)机制这个类的{@link#iterator()iterator}和
*{@link#lisiterator(int)lisiterator}方法是快速失败的:如果在迭代器运行后的任何时候对向量进行了结构修改
*以任何方式创建,除了通过迭代器自己的{@link ListIterator#remove()remove}或
*{@link ListIterator#add(Object)add}方法,迭代器将抛出{@link ConcurrentModificationException}。因此,面对
*并发修改时,迭代器会快速而干净地失败,而不是而不是冒着武断的、不确定的行为
*未来的时间。返回的{@link Enumerations}{@link#elements()elements}方法是<em>not</em>fail fast。
*<p>请注意,不能保证迭代器的快速失败行为一般说来,不可能在未来作出任何硬性保证
*存在未同步的并发修改。失败快速迭代器
*尽最大努力抛出{@code ConcurrentModificationException}。
*因此,编写依赖于此的程序是错误的其正确性例外:<i>迭代器的快速失败行为
*应仅用于检测错误。</i>
*<p>从Java2平台v1.2开始,这个类被改进为
*实现{@link List}接口,使其成为
*<a href=“{@docRoot}/./technotes/guides/collections/index.html”>
*Java集合框架</a>。与新系列不同
*实现时,{@code Vector}是同步的。如果线程安全
*不需要实现,建议使用{@link
*ArrayList}代替{@code Vector}。
 * @author  Lee Boynton
 * @author  Jonathan Payne
 * @see Collection
 * @see LinkedList
 * @since   JDK1.0
 */

主要体现在

  1. vector是动态扩容
  2. 为保证数据的安全使用快速失败机制
  3. 并且建议我们如果线程安全不需要实现,则使用arraylist。

实现接口

从接口来看,和arraylist的继承和实现是一致的。
继承自AbstractList
保证线程安全重写并add sublist等方法

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

成员变量

Vector 成员变量

 /**
    *将向量的分量放入其中的数组缓冲区已存储。向量的容量就是这个数组缓冲区的长度,
    *并且至少足够大以包含向量的所有元素。<p>向量中最后一个元素后面的任何数组元素都为空。
     */
    protected Object[] elementData;

   /**
    *此{@code Vector}对象中有效组件的数目。组件{@code elementData[0]}到
    *{@code elementData[elementCount-1]}是实际项。@序列号
    */
    protected int elementCount;

   /**
    *向量容量自动计算的量当其大小大于其容量时递增。如果容量增量小于或等于零,则容量
    *每次需要生长时,载体的体积都会加倍。
    *@序列号 
    */
    protected int capacityIncrement;

arraylist中的成员变量

 /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

不同点

  1. vector缺少 序列化优化的transient

  2. 不同构造方法构造不同的数组的DEFAULTCAPACITY_EMPTY_ELEMENTDATA

  3. 数组创建时间不同,vector在创建对象时就创建,arraylist则是修改队列是才改变

  4. vector默认可以指定容量的增长大小

相同点
都是object数组,初始化容量相同默认10

构造函数

  /**
     * 构造一个具有指定初始容量和容量增量。
     *
     * @param   initialCapacity     initialCapacity向量的初始容量
     * @param   capacityIncrement   capacityIncrement容量的增量
     *                              向量溢出时增加
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
      /**
     * Constructs an empty vector so that its internal data array
     * has size {@code 10} and its standard capacity increment is
     * zero.
     */
    public Vector() {
        this(10);
    }

创建初始化数组,及指定容量增加值

add方法

  1. 通过 synchronized 关键字去保证多线程下数据的安全
  2. modCount 记录好每次操作次数
  3. 在ensureCapacityHelper 中判断容量是否超出直接扩大两倍在来对比大小
  4. 扩大二倍还是不够,这选择扩大到增加数据所需的大小 ,
  5. 大于于 Integer.MAX_VALUE - 8,则直接使用 MAX_VALUE ,最后采用 Arrays.copyOf 进行扩容
 /**
     * 将指定的元素追加到此向量的末尾。
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
       /**
		*这实现了ensureCapacity的非同步语义。此类中的同步方法可以在内部调用一种在不增加生产成本的情   况下保证生产能力的方法额外的同步。
     *
     * @see #ensureCapacity(int)
     */
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  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);
    }

在这里插入图片描述

subList方法 重写 subList

   public synchronized List<E> subList(int fromIndex, int toIndex) {
        return Collections.synchronizedList(super.subList(fromIndex, toIndex),
                                            this);
    }

这里为保证线程安全,使用Collections.synchronizedList 将该对象包裹起来,保证数据安全
重写了AbstractList 提供的两个迭代器,以此来保证数据安全等

java.util集合结构图

在这里插入图片描述

最后

vector都是采用synchronized 作关键字通过对象锁来保证数据安全,这部分我解析的比较少,因为大部分和arraylist差不多,并且实现理解更简单,具体还是看arraylist中的解析,并且在官方文档中 Vector 已经过被弃用了 ,也推荐不使用,所以在业务开发过程中,也别使用vector

以上是关于Java 集合深入理解 :线程安全的数组集合(Vector)的主要内容,如果未能解决你的问题,请参考以下文章

cc分享java:深入理解java各种集合的线程安全

cc分享java:深入理解java各种集合的线程安全

深入理解JAVA集合系列:HashMap源码解读

深入理解JAVA集合系列二:ConcurrentHashMap源码解读

深入理解JAVA集合系列二:ConcurrentHashMap源码解读

Java中的集合和线程安全