浅谈Vector

浅谈Vector

在之前的文章中,我们已经说过线程不安全的ArrayList和LinkedList,今天我们来讲讲一个线程安全的列表容器,他就是Vector,他的底层和ArrayList一样使用数组来实现,不过不同于ArrayList的线程不安全,Vector中的公开方法基本都是带有synchronized关键字的,虽然有些方法是非同步的,但内部总是会调用同步的方法保证整个方法的线程安全,即使是subList方法返回的子列表也是通过Collections类的synchronizedList来保证返回的子列表也是线程安全的:

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

下面我们来介绍Vector的构造方法,Vector为我们提供了四种构造方法,前三种,基本就是无参调用单参,并传递一个默认容量值,单参调用双参,并传递一个默认容量值

Vector();

public Vector() {
    //调用单参的构造方法,并传递一个初始容量值10
    this(10);
}

Vector(int initialCapacity);

public Vector(int initialCapacity) {
    //调用双参的构造方法,并传递一个默认增量值0,增量值为0,代表每次扩容,容量会直接翻倍
    this(initialCapacity, 0);
}

Vector(int initialCapacity, int 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;
}

第四种构造方法,则是根据传入的集合对象来初始化矢量列表的元素

Vector(Collection<? extends E> c);

public Vector(Collection<? extends E> c) {
    elementData = c.toArray();
    elementCount = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    //如果通过toArray方法转换得到的数组类型不是Object[],则进行二次转化
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

在方法讲解之前,我们先来看看Vector的扩容机制,Vector提供了一个公开方法ensureCapacity(int minCapacity),通过该方法你可以传入一个列表需要的最小容量值(但最终生成的新容量可能并不是这个值),然后做一个简单的校验,进一步调用内部私有的ensureCapacityHelper(int minCapacity)方法进行进一步判断,如果该值小于列表当前的列表容量(非列表元素总数),不会进入到扩容方法grow中,进入grow方法后就开始了具体的扩容流程,具体请看下面的源码:

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,容量翻倍,否则增加增量变量指定的增量值
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
    //将新的容量值与传入的最小容量作比较,如果最小容量值大于新的容量值,则将最小容量值作为新的容量值,否则使用内置扩容规则获得的容量值
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //最后将容量值与数组最大容量值(最大整型值-8)做比较
    //数组作为一个对象,需要一定的内存存储对象头信息,对象头信息最大占用内存不可超过8字节
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //使用新容量和原列表生成新的列表并将元素进行拷贝,内部最终调用System.arraycopy方法进行元素拷贝
    elementData = Arrays.copyOf(elementData, newCapacity);
}

讲完Vector的扩容规则,下面正式进入Vector中方法的讲解

add(E element); addElement(E element);

这个两个方法都是往矢量列表尾部插入新的元素,都是同步方法,不同点是add的返回值为布尔类型,而addElement没有返回值

add(int index, E element); insertElementAt(E element, int index);

这两个方法可以说是完全一致,在add方法内只是进行了insertElementAt方法的调用,虽然add是非同步方法,但insertElementAt是一个同步方法

addAll([int index,] Collection<? extends E> c);

该方法用于往矢量列表中存入集合中的所有元素,该方法存在单参和双参两个重载方法,都是同步方法,单参方法直接往尾部插入,双参方法可以在指定索引位置插入

remove(int index);

该方法用于删除矢量列表中指定索引位置的值,并返回被删除的元素,该方法是同步方法

remove(Object o); removeElement(Object o); removeElementAt(int index);

这三个方法都是用于删除矢量列表中的元素,其中remove是非同步的,其余两个方法都是同步的,但在remove方法调用了同步的removeElement方法,在removeElement方法中先是通过indexOf方法查找待删除元素的索引,找到则调用removeElementAt方法删除指定索引位置的元素值

removeAll(Collection<?> c)

该方法用于删除传入集合中指定的所有元素,是一个同步方法

retainAll(Collection<?> c);

该方法用于保留传入集合中指定的所有元素,即删除指定集合之外的所有元素,该方法是一个同步方法

clear(); removeAllElements();

这两个方法都是用于删除矢量列表中的所元素,其中clear方法是非同步的,内部调用了同步的removeAllElements方法

removeRange(int fromIndex, int toIndex);

该方法用于移除矢量列表中指定索引范围的所有元素,该方法是一个同步方法

removeIf(Predicate<? super E> filter);

该方法用于移除矢量列表中符合指定条件的元素,该方法是一个同步方法,下面使用匿名内部类和Lambda做一个简单的使用演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
System.out.println(list);
//如果不会使用Lambda表达式,则需要new Predicate接口对象并实现内部的test方法
list.removeIf(x -> x < 3);
System.out.println(list);
//对应的运行结果
[1, 2, 3, 4, 5]
[3, 4, 5]

set(int index, E element); setElementAt(E element, int index);

这两个方法都是用于替换矢量列表中指定索引位置的元素值,都是同步方法,不同点是set方法返回被替换的元素值,而setElementAt没有返回值

replaceAll(UnaryOperator operator);

该方法用于对矢量列表中的所有元素进行指定钩子函数的操作,并且会保存对元素所做的修改,该方法是一个同步方法,下面使用匿名内部类结合Lambda表达式做一个简单的使用演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
System.out.println(list);
//或者使用UnaryOperator接口创建匿名内部类,并实现apply方法亦可
list.replaceAll(x -> x + 2);
System.out.println(list);
//对应的运行结果
[1, 2, 3, 4, 5]
[3, 4, 5, 6, 7]

forEach()

该方法用于对矢量列表中的所有元素进行指定钩子函数的操作,但是不会保存对元素所做的修改,该方法是一个同步方法,下面做简单演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
System.out.println(list);
//或者使用Consumer接口创建匿名内部类,并实现accept方法亦可
list.forEach(x -> System.out.println(x += 2));
System.out.println(list);
//对应的运行结果
[1, 2, 3, 4, 5]
3
4
5
6
7
[1, 2, 3, 4, 5]

get(int index); elementAt(int index);

这两个方法是一样的,用于返回矢量列表中指定位置的元素值,都是同步方法

elements();

该方法用于返回矢量列表的枚举对象,该方法是一个同步方法,可以使用返回的枚举对象进行简单迭代遍历,下面是简单应用:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
Enumeration<Integer> elements = list.elements();
while (elements.hasMoreElements()){
    System.out.println(elements.nextElement());
}

firstElement(); lastElement();

这两个方法分别用于返回矢量列表中的首元素和尾元素,都是同步方法

indexOf(Object o); lastIndexOf(Object o);

这两个方法分别用于返回矢量列表中第一次出现和最后一次出现指定元素的索引值,都是同步方法

lastIndexOf(Object o, int index);

该方法在lastIndexOf(Object o)的基础上添加了一个索引值,用于在指定索引值之前的位置查找最后一次出现指定元素的索引值,该方法是同步方法

iterator();

该方法用于返回矢量列表的普通迭代器对象,迭代器提供了hasNext,next,remove,forEachRemaining方法用于对元素进行操作,iterator方法是是一个同步方法

listIterator([int index]);

该方法用于返回矢量列表的增强迭代器对象,提供了普通迭代器的所有功能,此外还有hasPrevious,nextIndex,previousIndex,previous,set,add方法,而且还可以通过index参数指定起始迭代位置,listIterator方法是一个同步方法

spliterator();

该方法用于将矢量列表转化为可分割的数组对象,转化后的对象可以多次调用trySplit进行分割(五五分成),适合多线程的方式对大型矢量列表进行操作,简单演示拆分过程:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
list.add(6);list.add(7);list.add(8);list.add(9);list.add(0);
Spliterator<Integer> part1 = list.spliterator();
Spliterator<Integer> part2 = part1.trySplit();
Spliterator<Integer> part3 = part1.trySplit();
Spliterator<Integer> part4 = part2.trySplit();
part1.forEachRemaining(x-> System.out.print(x+" "));
System.out.println();
part2.forEachRemaining(x-> System.out.print(x+" "));
System.out.println();
part3.forEachRemaining(x-> System.out.print(x+" "));
System.out.println();
part4.forEachRemaining(x-> System.out.print(x+" "));
//对应的操作结果
8 9 0 
3 4 5 
6 7 
1 2 

capacity();

该方法用于返回矢量列表的容量值(elementData.length),该方法是一个同步方法

clone();

该方法用于返回一个矢量列表的浅克隆对象,该方法是同步方法

contains(Object o);

该方法用于判断矢量列表中是否含有指定元素,该方法是一个非同步方法,但内部调用同步的indexOf方法

containsAll(Collection<?> c);

该方法用于判断矢量列表中是否包含传入集合的所有元素,该方法是一个同步方法

copyInto(Object[] array);

该方法用于将矢量列表的全部元素值拷贝到传入的数组中,该方法是一个同步方法

equals(Object o);

该方法用于判断传入元素是否与当前元素相等(值相等,不一定非要是地址相等),该方法是同步方法

isEmpty();

该方法用于判断矢量列表中是否存在元素,主要根据elementCount属性值来判断,该方法是同步方法

setSize(int size);

该方法用于将矢量列表的元素总数控制在传入的size之内,如果size大于当前列表元素总数(elementCount),则将elementCount-size范围的null当做列表元素(从这里可以看出矢量列表中的元素存在与否不能根据是否为null来判断,而需要根据elementCount来判断),如果传入的size小于列表元素总数,则将size-elementCount的值都置为null,最后都将elementCount=size值,该方法是一个同步方法

size();

该方法用于返回矢量列表的元素总数(elementCount),该方法是一个同步方法

sort(Comparator<? super E> c);

该方法用于将矢量列表里的所有元素按照钩子函数的规则进行排序,该方法是一个同步方法,下面对该方法做简单演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(4);list.add(5);list.add(2);list.add(3);
list.add(8);list.add(9);list.add(0);list.add(6);list.add(7);
System.out.println(list);
//或者使用Comparator接口创建匿名内部类,并实现compare方法亦可
list.sort((o1,o2) -> o1 - o2);
System.out.println(list);

subList(int fromIndex, int toIndex);

该方法用于返回当前矢量列表指定范围内的子列表对象,该方法是一个同步方法,要注意的是最终返回的列表对象依旧是整个矢量列表对象,不过是添加了偏移量属性,所以对子列表的操作都会反映到父列表中

toArray(T[] a);

该方法用于获取矢量列表的数组对象,并转化为参数对应的数组类型,该方法是一个同步方法,以下为该方法的简单使用:

//如果直接调用list.toArray()方法,等同于list.toArray(new Object[0]);
String[] strs = list.toArray(new String[0]);

trimToSize();

该方法用于将矢量列表的容量调整为列表元素总数,调整中会生成新的数组对象,该方法为同步方法
如果对你有帮助,点个赞,或者打个赏吧,嘿嘿
整理不易,请尊重博主的劳动成果

原文地址:https://www.cnblogs.com/Mango-Tree/p/12737094.html