1.Vector介绍
2. 成员变量
3. 构造函数
4. 常用方法
4.1 增加
4.2 删除
4.3 修改
4.4 查询
4.5 其他常用函数
4.6 Lambda表达式相关的方法
4.7 如何遍历元素
5.序列化和反序列化
6.迭代器
6.1 Itr
6.2 ListItr
6.3 VectorSpliterator
7. 小结一下
1.Vector介绍
Vector
和前面说的ArrayList
很是类似,这里说的也是1.8版本,它是一个队列,但是本质上底层也是数组实现的。同样继承AbstractList
,实现了List
,RandomAcess
,Cloneable
, java.io.Serializable
接口。具有以下特点:
- 提供随机访问的功能:实现
RandomAcess
接口,这个接口主要是为List
提供快速访问的功能,也就是通过元素的索引,可以快速访问到。 - 可克隆:实现了
Cloneable
接口 - 是一个支持新增,删除,修改,查询,遍历等功能。
- 可序列化和反序列化
- 容量不够,可以触发自动扩容
- *最大的特点是:线程安全的,相当于线程安全的
ArrayList
。
定义源码如下:
public class Vector<E>extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
}
2. 成员变量
底层是数组,增加元素,数组空间不够的时候,需要扩容。
- elementData:真正保存数据的数组
- elementCount:实际元素个数
- capacityIncrement:容量增加系数,就是扩容的时候增加的容量
- serialVersionUID:序列化id
// 真正保存数据的数组
protected Object[] elementData;
// 元素个数
protected int elementCount;
//容量增加系数
protected int capacityIncrement;
// 序列化id
private static final long serialVersionUID = -2767605614048989439L;
3. 构造函数
Vector
一共有四个构造函数:
- 指定容量和增长系数
- 指定容量
- 不指定,使用默认容量值10
- 指定集合初始化
1.指定容量和增长系数构造函数
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.指定初始化容量,增长系数默认为0
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
3.什么都不指定,默认给的容量是10:
public Vector() {
this(10);
}
4.指定集合初始化:
public Vector(Collection extends E> c) {
// 转换成为数组
Object[] a = c.toArray();
// 大小为数组的大小
elementCount = a.length;
// 如果是ArrayList,则直接复制
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
// 否则需要进行拷贝
elementData = Arrays.copyOf(a, elementCount, Object[].class);
}
}
4. 常用方法
4.1 增加
增加元素,默认是在最后添加,如果容量不够的时候会触发扩容机制。
public synchronized void addElement(E obj) {
// 修改次数增加
modCount++;
// 确保容量足够(如果需要,里面会有扩容,复制操作)
ensureCapacityHelper(elementCount + 1);
// 将新元素放在最后一个元素,个数增加
elementData[elementCount++] = obj;
}
那么它是如何确保容量的呢?可以看到ensureCapacityHelper()
里面判断增加后的元素个数是否大于现在数组的长度,如果不满足,就需要扩容。调用grow()
函数扩容。
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;
// 如果新的容量比最大的容量还要大(虚拟机的数组大小是有最大值的)
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 需要处理把最大的容量降低一些
newCapacity = hugeCapacity(minCapacity);
// 拷贝数据
elementData = Arrays.copyOf(elementData, newCapacity);
}
在指定的索引index,插入数据,实际上调用的是insertElementAt(element, index)
.
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);
}
// 确保容量足够
ensureCapacityHelper(elementCount + 1);
// 拷贝数据,将后面的元素,往后移动一位
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
// 将实际的数据插入
elementData[index] = obj;
// 个数增加
elementCount++;
}
将一个集合所有元素添加进去:
public synchronized boolean addAll(Collection extends E> c) {
// 修改次数增加
modCount++;
// 转成数组
Object[] a = c.toArray();
// 数组的长度
int numNew = a.length;
// 确保容量足够
ensureCapacityHelper(elementCount + numNew);
// 拷贝
System.arraycopy(a, 0, elementData, elementCount, numNew);
// 更新个数
elementCount += numNew;
// 返回添加的数组是不是有数据
return numNew != 0;
}
指定index,插入一个集合,和前面不一样的地方在于复制之前,需要计算往后面移动多少位,不是用for循环去插入,而是一次性移动和写入。
public synchronized boolean addAll(int index, Collection extends E> c) {
// 修改次数增加
modCount++;
// 合法判断
if (index 0 || index > elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 转换数组
Object[] a = c.toArray();
// 插入数组长度
int numNew = a.length;
// 确保数组的长度是否合法
ensureCapacityHelper(elementCount + numNew);
// 移动的步长计算
int numMoved = elementCount - index;
if (numMoved > 0)
// 移动后面的元素,腾出位置给插入的元素
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 插入元素
System.arraycopy(a, 0, elementData, index, numNew);
// 更新个数
elementCount += numNew;
// 插入元素个数是否为0
return numNew != 0;
}
4.2 删除
删除指定元素
public boolean remove(Object o) {
return removeElement(o);
}
// 实际调用的是removeElement()
public synchronized boolean removeElement(Object obj) {
// 修改次数增加
modCount++;
// 获取第一个满足条件的元素缩影
int i = indexOf(obj);
// 索引如果满足条件
if (i >= 0) {
// 将索引为i的元素从数组中移除
removeElementAt(i);
return true;
}
return false;
}
// 操作数组删除元素
public synchronized void removeElementAt(int index) {
// 修改次数增加
modCount++;
// 是否合法
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
// 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 */
}
按照索引删除元素:
public synchronized E remove(int index) {
// 修改次数增加
modCount++;
// 合法性判断
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 保存原来的数据
E oldValue = elementData(index);
// 移动的个数
int numMoved = elementCount - index - 1;
// 如果移动个数大于0
if (numMoved > 0)
// 后面的元素往前面移动一位,赋值,覆盖
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 最后一个元素置空
elementData[--elementCount] = null; // Let gc do its work
// 返回旧的元素
return oldValue;
}
4.3 修改
下面两个set函数都是,修改索引为index的元素,区别就是一个会返回旧的元素,一个不会返回旧的元素。
public synchronized E set(int index, E element) {
// 合法性判断
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 取出旧的元素
E oldValue = elementData(index);
// 更新
elementData[index] = element;
// 返回旧的元素
return oldValue;
}
public synchronized void setElementAt(E obj, int index) {
// 合法性判断
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
// 直接更新
elementData[index] = obj;
}
4.4 查询
public synchronized E get(int index) {
// 合法判断
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
// 返回数组的元素
return elementData(index);
}
获取第一个元素:
public synchronized E firstElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(0);
}
获取最后一个元素:
public synchronized E lastElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(elementCount - 1);
}
E elementData(int index) {
return (E) elementData[index];
}
4.5 其他常用函数
将元素拷贝进数组中:
public synchronized void copyInto(Object[] anArray) {
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
手动缩容,其实就是将里面的数组复制到一个更小的数组,更新数组引用即可。
public synchronized void trimToSize() {
// 修改次数增加
modCount++;
// 获取数组的长度
int oldCapacity = elementData.length;
// 数组长度大于真实的容量,说明有可以缩容的空间
if (elementCount // 复制到新的数组
elementData = Arrays.copyOf(elementData, elementCount);
}
}
保证容量的函数,其实相当于手动扩容,参数是所需要的最小的容量,里面调用的ensureCapacityHelper()
在上面add()
函数解析的时候已经说过了,不再解析。
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
ensureCapacityHelper(minCapacity);
}
}
手动将元素个数设置为newSize,分为两种情况,一种是新的size比现在的size还要大,就是想到那个于指定容量扩容。另外一种是相当于缩容,但是这个缩容比较特殊,总的容量实际上没有变化,只是将里面多余的元素置为null。
public synchronized void setSize(int newSize) {
modCount++;
if (newSize > elementCount) {
// 扩容
ensureCapacityHelper(newSize);
} else {
for (int i = newSize ; i // 将超出个数的元素设置为null
elementData[i] = null;
}
}
elementCount = newSize;
}
获取容量:
public synchronized int capacity() {
return elementData.length;
}
获取里面真实的元素个数:
public synchronized int size() {
return elementCount;
}
容器是不是为空:
public synchronized boolean isEmpty() {
return elementCount == 0;
}
返回枚举类型的元素迭代器,这是一个有意思的方法,相当于用枚举包装了当前的元素,Enumeration
是一个接口,这个接口有两个方法,一个是hasMoreElements()
,表示是否有下一个元素。一个是nextElement()
,获取下一个元素。
public Enumeration elements() {
return new Enumeration() {int count = 0;// 重写方法,是否有下一个元素public boolean hasMoreElements() {return count }public E nextElement() {// 同步synchronized (Vector.this) {if (count // 返回下一个元素return elementData(count++);
}
}throw new NoSuchElementException("Vector Enumeration");
}
};
}
是否包含某一个元素,其实里面是获取对象的索引,如果索引大于等于0,证明元素在里面,否则元素不在里面。
public boolean contains(Object o) {
return indexOf(o, 0) >= 0;
}
返回元素的索引,分为两种情况,一种是元素是null的情况,不能使用equals()
方法,另一种是非null,可以直接使用equals()
方法。
public int indexOf(Object o) {
return indexOf(o, 0);
}
public synchronized int indexOf(Object o, int index) {
if (o == null) {
for (int i = index ; i if (elementData[i]==null)
return i;
} else {
for (int i = index ; i if (o.equals(elementData[i]))
return i;
}
return -1;
}
获取元素最后出现的索引位置,和前面一个不一样的是,这个需要从最后一个元素往前面查找
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
}
public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
if (o == null) {
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
拷贝元素,数组里面的元素其实拷贝的只是引用,如果修改新的Vector
里面的对象的属性,旧的也会被修改。
public synchronized Object clone() {
try {
@SuppressWarnings("unchecked")
Vector v = (Vector) super.clone();
v.elementData = Arrays.copyOf(elementData, elementCount);
v.modCount = 0;return v;
} catch (CloneNotSupportedException e) {// this shouldn't happen, since we are Cloneablethrow new InternalError(e);
}
}
比如:
class Student {
public int age;
public String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) {
Vector vector1 = new Vector<>();
vector1.add(new Student(1,"sam"));
Vector vector2 = (Vector) vector1.clone();
vector2.get(0).name = "change name";
System.out.println(vector2);
System.out.println(vector1);
}
输出结果如下,可以看出其实两个集合里面的Student还是同一个对象。
[Student{age=1, name='change name', score=0}]
[Student{age=1, name='change name', score=0}]
将元素转换成为数组,原理也是一样,都是浅拷贝,拷贝的都是元素对象的引用。
public synchronized Object[] toArray() {
return Arrays.copyOf(elementData, elementCount);
}
指定数组类型的拷贝:
public synchronized T[] toArray(T[] a) {if (a.length return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass());
System.arraycopy(elementData, 0, a, 0, elementCount);if (a.length > elementCount)
a[elementCount] = null;return a;
}
截取出某一段的元素集合,调用的是父类的方法
public synchronized List subList(int fromIndex, int toIndex) {
return Collections.synchronizedList(super.subList(fromIndex, toIndex),
this);
}
移除某一段索引的元素,我们可以看到首先是将后面的元素往前面移动,覆盖掉前面的元素,然后将后面的元素坑位赋值为null。
protected synchronized void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = elementCount - toIndex;
// 复制到前面一段,将被移除的那一段覆盖,相当于后面元素整体前移
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// Let gc do its work
int newElementCount = elementCount - (toIndex-fromIndex);
// 后面的坑位赋值为null
while (elementCount != newElementCount)
elementData[--elementCount] = null;
}
获取指定位置的迭代器:Vector
和ArrayList
基本差不多,都是定义了三个迭代器:
Itr
:实现接口Iterator
,有简单的功能:判断是否有下一个元素,获取下一个元素,删除,遍历剩下的元素ListItr
:继承Itr
,实现ListIterator
,在Itr
的基础上有了更加丰富的功能。VectorSpliterator
:可以分割的迭代器,主要是为了分割以适应并行处理。和ArrayList
里面的ArrayListSpliterator
类似。
// 返回指定index位置的ListIterator
public synchronized ListIterator listIterator(int index) {
if (index 0 || index > elementCount)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
// 返回开始位置的ListIterator
public synchronized ListIterator listIterator() {
return new ListItr(0);
}
// 返回Itr
public synchronized Iterator iterator() {
return new Itr();
}
// 返回VectorSpliterator
public Spliterator spliterator() {
return new VectorSpliterator<>(this, null, 0, -1, 0);
}
4.6 Lambda表达式相关的方法
- forEach:遍历处理
- removeIf:按照条件移除元素
- replaceAll:移除元素
- sort:排序
基本都是将行为当成参数传递到函数中进行处理,里面值得一提的是removeIf()
,里面是将过滤器传递进去,在里面我们可以看到使用了BitSet
,这个东西来保存了需要移除的元素的下标,统计完成之后,后面再取出来进行移除操作。那么这个BitSet
是什么呢???🤔🤔🤔🤔
一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。这和位向量(vector of bits)比较类似。这是一个传统的类,但它在Java 2中被完全重新设计。
这样一看其实就是一个保存位值的类,可以设置为true,也可以取出来,这样就比较符合现在的场景,先遍历一次,把需要移除的元素用BitSet
标记一下,然后再次遍历的时候,就复制元素,将这些坑位覆盖掉,就可以了。
@Override
public synchronized void forEach(Consumer super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int elementCount = this.elementCount;
for (int i=0; modCount == expectedModCount && i // 对每一个元素进行处理
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
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;
// 按照当前的大小创建一个位值保存BitSet
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
for (int i=0; modCount == expectedModCount && i @SuppressWarnings("unchecked")
final E element = (E) elementData[i];
// 如果符合条件
if (filter.test(element)) {
// 将指定索引处的位设置为 true。
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 // 返回第一个设置为 false 的位的索引,这发生在指定的起始索引或之后的索引上。
i = removeSet.nextClearBit(i);
// 元素前移操作,覆盖被移除的元素的位置
elementData[j] = elementData[i];
}
// 将后面的元素坑位置为null
for (int k=newSize; k elementData[k] = null; // Let gc do its work
}
elementCount = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
@Override
@SuppressWarnings("unchecked")
public synchronized void replaceAll(UnaryOperator operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = elementCount;
for (int i=0; modCount == expectedModCount && i // operator是操作,意思是将改操作应用于里面的每一个元素
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
@SuppressWarnings("unchecked")
@Override
public synchronized void sort(Comparator super E> c) {
final int expectedModCount = modCount;
// 底层其实就是调用了数组的排序方法,将比较器c传递进去
Arrays.sort((E[]) elementData, 0, elementCount, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
4.7 如何遍历元素
遍历方法有以下几种:值得一说的是使用迭代器和使用枚举迭代器进行遍历。
Vector myVector = new Vector<>();// 第一种for(String item:myVector){
System.out.println(item);
}// 第二种
myVector.forEach(item-> System.out.println(item));
myVector.stream().forEach(new Consumer() {@Overridepublic void accept(String s) {
System.out.println(s);
}
});// 第三种for(int index = 0;index System.out.println(myVector.get(index));
}// 第四种
Iterator iterator = myVector.iterator();while(iterator.hasNext()){
System.out.println((String)iterator.next());
}// 第五种
Enumeration enumeration = myVector.elements();while(enumeration.hasMoreElements()){
System.out.println(enumeration.nextElement().toString());
}
5.序列化和反序列化
其实我们可以看到它的元素集合没有用transient
来修饰,和ArrayList
有所不同。
protected Object[] elementData;
但是它也重写了序列化的readObject()
和writeObject()
两个方法。和ArrayList
不同的是,序列化的时候将所有的数组里面的元素都序列化了,更加占用空间。序列化的时候会序列化三个东西:
- capacityIncrement:扩容增长系数
- elementCount:元素个数
- elementData: 数组元素
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();
}
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();
}
6.迭代器
Vector
和ArrayList
基本差不多,都是定义了三个迭代器:
Itr
:实现接口Iterator
,有简单的功能:判断是否有下一个元素,获取下一个元素,删除,遍历剩下的元素ListItr
:继承Itr
,实现ListIterator
,在Itr
的基础上有了更加丰富的功能。VectorSpliterator
:可以分割的迭代器,主要是为了分割以适应并行处理。和ArrayList
里面的ArrayListSpliterator
类似。
6.1 Itr
Itr
这是一个比较初级的迭代器,实现了Iterator
接口,有判断是否有下一个元素,访问下一个元素,删除元素的方法以及遍历对每一个元素处理的方法。里面有两个比较重要的属性:
- cursor:下一个即将访问的元素下标
- lastRet:上一个返回的元素下标,初始化为-1
两个重要的方法:
- next():获取下一个元素
- remove():移除当前元素,需要在next()方法调用之后,才能调用,要不会报错。
和ArrayList
里面定义的基本差不多,除了这里面其实加上同步,因为要做到线程安全。
private class Itr implements Iterator<E> {
// 下一个即将返回的元素index
int cursor;
// 上一个返回的index,-1则表示没有
int lastRet = -1;
int expectedModCount = modCount;
// 是否还有下一个元素
public boolean hasNext() {
return cursor != elementCount;
}
// 获取下一个返回的元素
public E next() {
// 同步
synchronized (Vector.this) {
checkForComodification();
// 由于cursor本身就是下一个元素的下标,所以这个值直接取到,返回就可以,用i保存一下
int i = cursor;
if (i >= elementCount)
throw new NoSuchElementException();
// 下一个返回的index更新
cursor = i + 1;
// 返回i位置的值,更新lastRet位置
return elementData(lastRet = i);
}
}
// 移除元素
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
// 同步
synchronized (Vector.this) {
checkForComodification();
// 调用Vector的移除方法
Vector.this.remove(lastet);
expectedModCount = modCount;
}
// 删除了当前的元素,相当于迭代器倒退了一步
cursor = lastRet;
// 上次返回的元素下标更新为-1,因为移除了
lastRet = -1;
}
// 遍历处理剩下的元素
@Override
public void forEachRemaining(Consumer super E> action) {
Objects.requireNonNull(action);
synchronized (Vector.this) {
final int size = elementCount;
int i = cursor;
if (i >= size) {
return;
}
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) Vector.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
// 对剩下的元素挨个处理
while (i != size && modCount == expectedModCount) {
action.accept(elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}
// 检查是否被修改
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
6.2 ListItr
拓展了Itr
的功能,多了几个方法。主要增加的功能有:
- 根据index获取该位置的迭代器
- 判断是否有前面的元素
- 获取下一个返回元素的下标
- 获取上一个返回元素的下面
- 获取上一个元素
- 更新元素
- 增加元素
基本和ArrayList
的也一样,也就修改的方法上加上了synchronized
关键字进行同步。
final class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
// 是否有上一个元素
public boolean hasPrevious() {
return cursor != 0;
}
// 下一个元素下标
public int nextIndex() {
return cursor;
}
// 上一个元素下标
public int previousIndex() {
return cursor - 1;
}
// 获取上一个元素
public E previous() {
// 同步
synchronized (Vector.this) {
checkForComodification();
int i = cursor - 1;
if (i 0)
throw new NoSuchElementException();
// 倒退了一步,所以cursor相当于减1
cursor = i;
// 更新上一个元素index
return elementData(lastRet = i);
}
}
// 更新元素
public void set(E e) {
if (lastRet == -1)
throw new IllegalStateException();
synchronized (Vector.this) {
checkForComodification();
Vector.this.set(lastRet, e);
}
}
// 插入元素
public void add(E e) {
int i = cursor;
synchronized (Vector.this) {
checkForComodification();
Vector.this.add(i, e);
expectedModCount = modCount;
}
// 插入元素之后,下一个元素的下标相当加1,因为它们相当于后移了
cursor = i + 1;
lastRet = -1;
}
}
6.3 VectorSpliterator
直接看源码,这是一个用来适应多线程并行迭代的迭代器,可以将集合分成多端,进行处理,每一个线程执行一段,那么就不会相互干扰,它可以做到线程安全。
对标ArrayListSpliterator
,里面的实现基本一样。
static final class VectorSpliterator<E> implements Spliterator<E> {
private final Vector list;private Object[] array;// 当前位置private int index;// 结束位置,-1表示最后一个元素private int fence; // -1 until used; then one past last indexprivate int expectedModCount; // initialized when fence set/** Create new spliterator covering the given range */
VectorSpliterator(Vector list, Object[] array, int origin, int fence,int expectedModCount) {this.list = list;this.array = array;this.index = origin;this.fence = fence;this.expectedModCount = expectedModCount;
}private int getFence() { // initialize on first useint hi;if ((hi = fence) 0) {synchronized(list) {
array = list.elementData;
expectedModCount = list.modCount;
hi = fence = list.elementCount;
}
}return hi;
}// 分割,每调用一次,将原来的迭代器等分为两份,并返回索引靠前的那一个子迭代器。public Spliterator trySplit() {int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;return (lo >= mid) ? null :new VectorSpliterator(list, array, lo, index = mid,
expectedModCount);
}// 返回true时,表示可能还有元素未处理// 返回falsa时,没有剩余元素处理了@SuppressWarnings("unchecked")public boolean tryAdvance(Consumer super E> action) {int i;if (action == null)throw new NullPointerException();if (getFence() > (i = index)) {
index = i + 1;
action.accept((E)array[i]);if (list.modCount != expectedModCount)throw new ConcurrentModificationException();return true;
}return false;
}// 遍历处理剩下的元素@SuppressWarnings("unchecked")public void forEachRemaining(Consumer super E> action) {int i, hi; // hoist accesses and checks from loop
Vector lst; Object[] a;if (action == null)throw new NullPointerException();if ((lst = list) != null) {if ((hi = fence) 0) {synchronized(lst) {
expectedModCount = lst.modCount;
a = array = lst.elementData;
hi = fence = lst.elementCount;
}
}else
a = array;if (a != null && (i = index) >= 0 && (index = hi) <= a.length) {while (i action.accept((E) a[i++]);if (lst.modCount == expectedModCount)return;
}
}throw new ConcurrentModificationException();
}// 估算大小public long estimateSize() {return (long) (getFence() - index);
}// 返回特征值public int characteristics() {return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
几个迭代器,各有各自的功能,我们按需使用即可🌝🌝🌝
7. 小结一下
Vector
的思路和ArrayList
基本是相同的,底层是数组保存元素,Vector
默认的容量是10,有一个增量系数,如果指定,那么每次都会增加一个系数的大小,否则就扩大一倍。
扩容的时候,其实就是数组的复制,其实还是比较耗时间的,所以,我们使用的时候应该尽量避免比较消耗时间的扩容操作。
和ArrayList最大的不同,是它是线程安全的,几乎每一个方法都加上了Synchronize
关键字,所以它的效率相对也比较低一点。ArrayList如果需要线程安全,可以使用List list = Collections.synchronizedList(new ArrayList(...));
这个方法。
此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~
技术之路不在一时,山高水长,纵使缓慢,驰而不息。
- END -