1. Vector简介

  Vector是JDK1.0版本就推出的一个类,和ArrayList一样,继承自AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable接口,底层也是基于数组实现的,不同的是它是一个线程安全的类。

2. Vector实现

1. 核心属性

//底层存储数组
protected Object[] elementData;
//数组内实际存储元素个数
protected int elementCount;
//扩容容量
protected int capacityIncrement;

  与ArrayList相比,核心属性多了一个扩容容量,可在初始化时指定,若不指定默认扩容2倍。  

2. 构造函数

    //initialCapacity:指定初始容量,capacityIncrement:指定扩容容量
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
} //只指定初始容量,每次扩容2倍
public Vector(int initialCapacity) {
this(initialCapacity, 0);
} //无参构造,默认初始容量10,每次扩容2倍
public Vector() {
this(10);
} //传入一个集合c,初始容量与c等于c的大小,每次扩容2倍
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);
}

3. 存储元素

  和ArrayList不同的是,Vector里每个存储元素的方法都是synchronized修饰的,所以Vector是线程安全的,但是由于锁的存在,效率会比较低。

  当对Vector进行元素添加的时候,都会检查底层数组的容量是否足够,若是不够则进行自动扩容,每次对数组进行增删改的时候都会增加modCount(继承自AbstractList的属性,用来统计修改次数),添加单个元素时一般会扩容2倍(如果构造函数中指定了扩容容量则以指定容量进行扩容),添加集合时则要取要添加的集合大小与自动扩容的大小的最大值进行扩容。

  存储元素分为三种类型:追加,插入,替换,其中第二种和第三种类型都会检查下标是否越界(根据size属性而不是数组的长度)

  (1)追加:这种方式最常用,直接添加到数组中最后一个元素的后面。

    //追加一个元素,返回布尔值,同步方法 synchronized作用于普通方法相当于synchroniced(this),锁的是当前对象
public synchronized boolean add(E e) {
modCount++;
     //容量小于 elementCount+1 时自动扩容
ensureCapacityHelper(elementCount + 1);
     //将元素追加到数组末端的同时将elementCount+1
elementData[elementCount++] = e;
return true;
} //追加一个集合,返回布尔值,同步方法
public synchronized boolean addAll(Collection<? extends E> c) {
modCount++;
     //将追加的集合转为数组
Object[] a = c.toArray();
     //取追加的元素个数
int numNew = a.length;
     //容量小于 elementCount+numNew 时自动扩若
ensureCapacityHelper(elementCount + numNew);
   //通过System.arraycopy将集合追加到当前对象底层数组末端,效率较低
System.arraycopy(a, 0, elementData, elementCount, numNew);
     //将追加的数组个数加进elementCount
elementCount += numNew;
return numNew != 0;
} //添加一个元素,无返回值 add(E e)是在List中定义的方法,并且有返回值,同步方法
  //该方法在其子类Stack中被调用,感觉和add方法没啥区别??
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}

  (2)插入:当调用下面这两个方法向数组中添加元素或集合时,会先查找索引位置,然后将元素添加到索引处,最后把添加前索引后面的元素追加到新元素的后面。

    //在指定位置插入一个元素,虽然没有加synchronized,但方法内调用的方法是synchronized修饰的,不明白为何不把这两个方法合并
public void add(int index, E element) {
insertElementAt(element, index);
}
//在指定位置插入元素,同步方法
public synchronized void insertElementAt(E obj, int index) {
modCount++;
     //检查下标是否越界,越界则抛出异常
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
}
     //容量不足 elementCount+1 时自动扩容
ensureCapacityHelper(elementCount + 1);
     //将元素index下标位置及后面的元素全部向后移一位
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
     //在index下标位置插入元素
elementData[index] = obj;
elementCount++;
} //在指定位置插入集合,同步方法
public synchronized boolean addAll(int index, Collection<? extends E> c) {
modCount++;
     //检查下标是否越界,越界则抛出异常
if (index < 0 || index > elementCount)
throw new ArrayIndexOutOfBoundsException(index);
     //将需要插入的集合c转为数组a
Object[] a = c.toArray();
     //将数组a的大小赋值给numNew
int numNew = a.length;
     //当容量小于 elementCount + numNew 时自动扩若
ensureCapacityHelper(elementCount + numNew);
     //获取插入位置index下标及其后面的元素个数
int numMoved = elementCount - index;
if (numMoved > 0)
       //将elementData从下标为index处开始的numMoved个元素复制到elementData的下标为 index+numNew 处及其后面
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
     //将数组a从下标为0开始的numNew个元素复制到elementData的下标为index处
System.arraycopy(a, 0, elementData, index, numNew);
elementCount += numNew;
return numNew != 0;
}

  (3)替换:调用该方法会将index位置的元素用新元素替代。

    //将下标为index处替换成当前元素,同步方法
public synchronized E set(int index, E element) {
     //检查下标是否越界,越界则抛出异常
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
     //取出index下标处的元素
E oldValue = elementData(index);
     //将index下标处元素替换
elementData[index] = element;
     //返回被替换的元素
return oldValue;
} //直接替换,无返回值,同步方法
public synchronized void setElementAt(E obj, int index) {
     //检查下标是否越界,越界则抛出异常
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
     //将index处元素直接替换
elementData[index] = obj;
}

4. 元素读取  

    //获取指定下标位置的元素,同步方法
public synchronized E get(int index) {
//检查下标是否越界,越界则抛出异常
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
//取出底层存储数组index下标处的元素并返回
return elementData(index);
}

5. 元素删除

  (1) 按下标移除:

    //重写 同步方法,返回被移除元素
public synchronized E remove(int index) {
modCount++;
//检查下标是否越界,越界则抛出异常
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
//取出指定下标位置的元素
E oldValue = elementData(index);
//算出指定下标位置后面的数据个数
int numMoved = elementCount - index - 1;
//如果该位置不是最后一位,则把后面的所有元素左移一位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//将最后一位置空的同时把elementCount - 1
elementData[--elementCount] = null; // Let gc do its work return oldValue;
} //扩展 同步方法,无返回值
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
//比remove方法多了index < 0的情况的下标检查
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}

  (2)移除指定元素

    //重写方法 实则调用内部扩展的方法removeElement
public boolean remove(Object o) {
return removeElement(o);
}
//扩展 同步方法,返回布尔值
public synchronized boolean removeElement(Object obj) {
modCount++;
//取指定元素的下标
int i = indexOf(obj);
if (i >= 0) {
//将改下标的元素移除,复用removeElementAt的逻辑
removeElementAt(i);
return true;
}
return false;
}

  (3)移除多个元素  

    //移除指定元素集合,复用父类的移除逻辑,重写的目的只是为了加锁
public synchronized boolean removeAll(Collection<?> c) {
return super.removeAll(c);
}
//移除某个范围的所有元素,扩展 同步方法
protected synchronized void removeRange(int fromIndex, int toIndex) {
     modCount++;
     //获取被移除范围后面的元素个数
     int numMoved = elementCount - toIndex;
     //把fromIndex后面的元素全部移到toIndex位置及其后面
  System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);   // Let gc do its work
  int newElementCount = elementCount - (toIndex-fromIndex);
     //将大于等于新数组元素个数的下标元素全部置空
   while (elementCount != newElementCount)
  elementData[--elementCount] = null;
  }

  

  (4)移除所有元素

    //扩展 同步方法,清空底层数组并将数量置0,无返回值
public synchronized void removeAllElements() {
modCount++;
// Let gc do its work
for (int i = 0; i < elementCount; i++)
elementData[i] = null; elementCount = 0;
}
//重写方法,为了加锁
public void clear() {
  removeAllElements();
}

  

  (5)根据lambda表达式条件移除:removeIf

    public synchronized boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final int size = elementCount;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
} // shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
elementCount = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
} return anyToRemove;
}
   //用法如下:把大于10的元素移除调用
  Vector<Integer> vector = new Vector<Integer>();
vector.add(5);
vector.add(6);
vector.add(11);
vector.add(12);
vector.removeIf(Integer -> Integer > 10);

6. 调整数组容量

  (1)扩容:手动扩容和自动扩容,手动扩容用于加入Vector的数据量较大时,提前扩容至需要的容量,避免添加时递增式反复调用System.arraycopy带来的性能损耗,自动扩容则由添加元素时内存不够时触发。

    //对外开放的同步方法,提供手动扩容的功能,内部没有进行调用
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
ensureCapacityHelper(minCapacity);
}
}
//私密方法,内部自动扩容调用
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;
//如果构造函数传入了扩容容量并且大于0,则按照指定的扩容容量进行扩容,否则默认扩容2倍(这里是本方法和ArrayList的唯一区别之处)
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
//如果扩容后的容量小于新数组需要的最小容量,则扩容至新数组需要的最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//处理扩容后的容量大于预设的list最大值的情况
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
//大于int最大值,抛出内存溢出异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//新数组需要的最小容量大于预设的list最大值则扩容至int的最大值,否则扩容至预设的list最大值
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

  (2)缩容:就是把底层数组的长度调整为元素实际个数大小,这个方法主要是为了防止Vector进行扩容时产生的空间浪费。

    //同步方法
public synchronized void trimToSize() {
modCount++;
//获取底层数组的长度
int oldCapacity = elementData.length;
//当元素个数小于数组长度时,通过System.arraycopy把空的元素全部去除
if (elementCount < oldCapacity) {
elementData = Arrays.copyOf(elementData, elementCount);
}
}

7. 转为静态数组

    //同步方法,返回Object类型数组
public synchronized Object[] toArray() {
return Arrays.copyOf(elementData, elementCount);
}
//同步方法,返回指定类型和大小的数组,如:new Integer[2]
public synchronized <T> T[] toArray(T[] a) {
//如果传入数组a大小小于底层数组elementData元素个数,则直接复制
if (a.length < elementCount)
return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass());
//相反,则把数组中的元素全部复制到数组a中
System.arraycopy(elementData, 0, a, 0, elementCount);
//这一步不清楚为何要这么做,如果a.length=3,elementCount=1,也只会把下标为1位置的元素置空,没管下标为2的位置,有点费解
if (a.length > elementCount)
a[elementCount] = null; return a;
}

8.实现了Cloneable接口,进行数据浅拷贝

    public synchronized Object clone() {
try {
@SuppressWarnings("unchecked")
Vector<E> v = (Vector<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, elementCount);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}

9.实现Serializable 接口,启用其序列化功能

    private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
final java.io.ObjectOutputStream.PutField fields = s.putFields();
final Object[] data;
synchronized (this) {
fields.put("capacityIncrement", capacityIncrement);
fields.put("elementCount", elementCount);
data = elementData.clone();
}
fields.put("elementData", data);
s.writeFields();
} private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gfields = in.readFields();
int count = gfields.get("elementCount", 0);
Object[] data = (Object[])gfields.get("elementData", null);
if (count < 0 || data == null || count > data.length) {
throw new StreamCorruptedException("Inconsistent vector internals");
}
elementCount = count;
elementData = data.clone();
}

10. 实现了RandomAccess接口,启用随机访问

  通过实现RandomAccess接口表明该类支持随机访问,可以采用for循环的方式进行遍历来提高性能。

 

学习JDK1.8集合源码之--Vector的更多相关文章

  1. 学习JDK1.8集合源码之--LinkedHashSet

    1. LinkedHashSet简介 LinkedHashSet继承自HashSet,故拥有HashSet的全部API,LinkedHashSet内部实现简单,核心参数和方法都继承自HashSet,只 ...

  2. 学习JDK1.8集合源码之--ArrayList

    参考文档: https://cloud.tencent.com/developer/article/1145014 https://segmentfault.com/a/119000001857894 ...

  3. 学习JDK1.8集合源码之--WeakHashMap

    1. WeakHashMap简介 WeakHashMap继承自AbstractMap,实现了Map接口. 和HashMap一样,WeakHashMap也是一种以key-value键值对的形式进行数据的 ...

  4. 学习JDK1.8集合源码之--HashMap

    1. HashMap简介 HashMap是一种key-value结构存储数据的集合,是map集合的经典哈希实现. HashMap允许存储null键和null值,但null键最多只能有一个(HashSe ...

  5. 学习JDK1.8集合源码之--ArrayDeque

    1. ArrayDeque简介 ArrayDeque是基于数组实现的一种双端队列,既可以当成普通的队列用(先进先出),也可以当成栈来用(后进先出),故ArrayDeque完全可以代替Stack,Arr ...

  6. 学习JDK1.8集合源码之--HashSet

    1. HashSet简介 HashSet是一个不可重复的无序集合,底层由HashMap实现存储,故HashSet是非线程安全的,由于HashSet使用HashMap的Key来存储元素,而HashMap ...

  7. 学习JDK1.8集合源码之--Stack

    1. Stack简介 Stack是集合中对数据结构栈的一种实现,栈的原则是先进先后出,与队列相反(先进先出).Stack是继承自Vector的,意味着它也是由数组实现的线程安全的,不考虑线程安全的情况 ...

  8. 学习JDK1.8集合源码之--TreeMap

    1. TreeMap简介 TreeMap继承自AbstractMap,实现了NavigableMap.Cloneable.java.io.Serializable接口.所以TreeMap也是一个key ...

  9. 学习JDK1.8集合源码之--LinkedHashMap

    1. LinkedHashMap简介 LinkedHashMap继承自HashMap,实现了Map接口. LinkedHashMap是HashMap的一种有序实现(多态,HashMap的有序态),可以 ...

随机推荐

  1. Spring Boot 集成Jsp与生产环境部署

    一.简介 提起Java不得不说的一个开发场景就是Web开发,也是Java最热门的开发场景之一,说到Web开发绕不开的一个技术就是JSP,因为目前市面上仍有很多的公司在使用JSP,所以本文就来介绍一下S ...

  2. maven 项目在 tomcat 中启动报错:Caused by: java.util.zip.ZipException: invalid LOC header (bad signature)

    问题原因: 在下载 maven 依赖包的时候出现某种原因导致下载的依赖包出现损坏,jvm 和 maven 不能正常识别,从而导致出现该问题. 解决办法: 在 maven 仓库中搜索: in-progr ...

  3. jaxFileUpload插件异步上传图片

    第一步:引入jquery文件和jaxFileUpload文件 文件位置:https://pan.baidu.com/s/1jHEyIyy 第二步,前端: <div class="for ...

  4. spring cloud深入学习(十一)-----服务网关zuul

    前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,似乎一个 ...

  5. Android Studio增加assets目录、raw目录

    assets与res/raw不同 assets目录是Android的一种特殊目录,用于放置APP所需的固定文件,且该文件被打包到APK中时,不会被编码到二进制文件. Android还存在一种放置在re ...

  6. [转载] DSP6000图像位移与变形典型算法

    原文地址:转载:DSP6000图像位移与变形典型算法作者:Jane 李现路:DSP6000图像位移与变形典型算法 一.图像的平移算法 图像平移的数学表达式原理: 初始坐标为(x0,y0)的点经过平移( ...

  7. Http请求中的Content-Type

    转载至:https://segmentfault.com/a/1190000013056786?utm_source=tag-newest 一 前言 ----现在搞前端的不学好http有关的知识已经不 ...

  8. docker tomcat启动慢

    镜像 https://hub.docker.com/r/errorlife/tomcat/ docker pull errorlife/tomcat

  9. 1.开始Spring

    1 对Spring的认识 为了实现控制反转,我们可以想象java创建了一个工厂类,工厂类可以去读取配置文件 比如一个.property文件.通过这个文件中的配置,反射创建一些对象,当外界需要某个类的对 ...

  10. Linq处理list数据

    获取数据列表. //获取数据列表,Model是类 IList<Model> list = dao.getmx(Model, pageInfo);//DataTable数据DataTable ...