1. Обзор вектора
Представляя Vector, люди часто говорят:
底层实现与 ArrayList 类似,不过Vector 是线程安全的,而ArrayList 不是。
Так правильно ли это определение? Давайте проанализируем его в сочетании с предыдущей статьей:
Коллекции Java, серия 1, ArrayList
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Vectorэто векторная очередь, зависимости которой такие же, какArrayListявляется непротиворечивым, поэтому он имеет следующие функции:
- 1,Serializable: поддерживает сериализацию объектов.Хотя переменные-члены не изменяются с помощью ключевого слова transient, Vector по-прежнему реализует метод writeObject() для сериализации.
- 2,Cloneable: переопределите метод clone() для копирования массива через Arrays.copyOf().
- 3.RandomAccess: Обеспечивает функцию произвольного доступа, мы можем быстро получить объект элемента через серийный номер элемента.
- 4.AbstractList: наследует AbstractList, указывая, что это список с соответствующими функциями, такими как добавление, удаление, проверка и изменение.
- 5.List: Оставьте вопрос, зачем вам реализация интерфейса List, если вы наследуете AbstractList?
* Расширение мышления: Почему сериализация Vector переопределяет только метод writeObject()?
Осторожные друзья, если вы посмотрите исходный код вектора, то обнаружите, что в комментариях к writeObject() есть такое утверждение:
This method performs synchronization to ensure the consistency
of the serialized data.
После прочтения комментариев у вас может возникнуть внезапное осознание, а не является ли основной идеей потокобезопасности Vector? Затем процесс сериализации должен быть заблокирован для работы, чтобы можно было сказать, что он является потокобезопасным. Таким образом, даже если никакие elementData не украшены переходными процессами, вам все равно нужно переопределить writeObject().
* Расширение мышления: Так же, как ArrayLit и большинство классов коллекций, зачем вам нужно реализовывать интерфейс List, если вы наследуете AbstractList?
Есть две теории, вы можете обратиться к:
1. В StackOverFlow:порталОтветчик с наибольшим количеством голосов сказал, что спросил Джоша Блоха, который написал код, и узнал, что это опечатка.
2. getInterfaces класса Class может получить реализованный интерфейс, но не может получить интерфейс реализации родительского класса, но эта операция бессмысленна.
2. Переменная-член вектора
/**
与 ArrayList 中一致,elementData 是用于存储数据的。
*/
protected Object[] elementData;
/**
* The number of valid components in this {@code Vector} object.
与ArrayList 中的size 一样,保存数据的个数
*/
protected int elementCount;
/**
* 设置Vector 的增长系数,如果为空,默认每次扩容2倍。
*
* @serial
*/
protected int capacityIncrement;
// 数组最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
По сравнению с переменными-членами в ArrayList, у Vector на два пустых объекта массива меньше:EMPTY_ELEMENTDATAиDEFAULTCAPACITY_EMPTY_ELEMENTDATA
Итак, первое различие между Vector и ArrayList заключается в том,переменные-члены несовместимы.
3. Векторный конструктор
Vector предоставляет четыре конструктора:
- Vector(): конструктор по умолчанию
- Vector(int initialCapacity):capacity — размер емкости по умолчанию для Vector. Когда емкость увеличивается за счет добавления данных, емкость каждый раз удваивается.
- Vector(int initialCapacity, int capacityIncrement): емкость — это размер емкости Вектора по умолчанию, а емкостьИнкремент — это возрастающее значение каждый раз, когда емкость Вектора увеличивается.
- Vector(Collection<? extends E> c): Создайте вектор, содержащий коллекцию
На первый взгляд конструкторы, предоставляемые в Vector, так же богаты, как и в ArrayList. Но когдаСодержание предыдущего разделаРазобрав конструктор ArrayList в , и посмотрев на конструктор Vector, вы почувствуете уныние.
//默认构造函数
public Vector() {
this(10);
}
//带初始容量构造函数
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//带初始容量和增长系数的构造函数
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
В коде вроде бы не так много проблем, он такой же, как код, который мы обычно пишем, но ему не хватает шарма по сравнению с конструктором в ArrayList. Заинтересованные студенты могут взглянуть на реализацию конструктора в ArrayList.
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);
}
После JDK 1.2 был предложен конструктор для преобразования Collection в Vector, фактическая операция которого заключается в копировании содержимого массива Collection в объект Vector через Arrays.copyOf(). можно броситьNullPointerException.
В отличие от конструктора: конструктор Vector уступает ArrayList.
4. Добавить метод (Добавить)
Vector имеет на один метод больше, чем ArrayList, в способе добавления элементов. Методы добавления, поддерживаемые Vector:
- add(E)
- addElement(E)
- add(int i , E element)
- addAll(Collection<? extends E> c)
- addAll(int index, Collection<? extends E> c)
4.1 addElement(E)
Давайте посмотрим на это дополнениеaddElement(E)Что особенного в методах:
/**
* Adds the specified component to the end of this vector,
* increasing its size by one. The capacity of this vector is
* increased if its size becomes greater than its capacity.
*
* <p>This method is identical in functionality to the
* {@link #add(Object) add(E)}
* method (which is part of the {@link List} interface).
*
* @param obj the component to be added
*/
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
Судя по комментариям выше, этот метод имеет ту же функцию, что и метод add(E). Поэтому ничего особенного, кроме того, что возвращаемые данные другие.
Мы следуем приведенному выше коду, чтобы проанализировать метод сложения в Vector. Вы можете видеть, что Vector блокирует весь метод добавления (добавлениеsynchronizedмодификация), на самом деле мы можем понять, что процесс добавления элементов в основном включает в себя следующие операции:
- sureCapacityHelper(): Подтвердите размер контейнера
- Grow(): увеличить контейнер, если это необходимо.
- elementData[elementCount++] = obj: установить значение
Во избежании многопоточности, когда емкость sureCapacityHelper не нужно расширять, другие потоки просто заполняют массив, который появится в это время.ArrayIndexOutOfBoundsException, поэтому блокировка всего метода предотвращает это.
По сравнению с ArrayList на этапе подтверждения размера контейнера меньшеArrayList#ensureCapacityInternalЭтот шаг в основном связан с тем, что при построении вектора и создании размера массива по умолчанию массив не будет пустым.
Далее в методе Grow():
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//区别与ArrayList 中的位运算,这里支持自定义增长系数
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);
}
Vector поддерживает пользовательские коэффициенты роста, а также есть вadd()Одно из немногих ярких пятен в методе.
4.2 add(int index, E element)
Эта часть кода не сильно отличается от кода в ArrayList, главным образом потому, что выбрасывается другое исключение, в ArrayList выдается IndexOutOfBoundsException. Здесь выдается исключение ArrayIndexOutOfBoundsException. Что касается того, почему вам нужно извлечь операцию в метод insertElementAt()? Детская обувь может подумать об этом.
/**
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index > size()})
* @since 1.2
*/
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++;
}
В методе сложения Vector похож на ArrayList. У вектора есть странный addElement(E).
5. Удалить метод (Удалить)
В Vector предусмотрено много методов удаления, но если вы посмотрите на исходный код, вы обнаружите, что большинство из них — одни и те же методы.
- remove(int location)
- remove(Object object)
- removeAll(Collection<?> collection)
- removeAllElements()
- removeElement(Object object)
- removeElementAt(int location)
- removeRange(int fromIndex, int toIndex)
- clear()
5.1 удалить (целое местоположение) & removeElementAt (целое местоположение)
Сравниватьremove(int location)иremoveElementAt(int location)
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (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 */
}
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);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
За исключением разных возвращаемых типов данных, другие внутренние операции фактически одинаковы. remove — это операция, которая переопределяет родительский класс, а removeElement — это пользовательский метод в Vector. Метод fastRemove() предоставляется в ArrayList, который имеет тот же эффект, но область действия removeElement общедоступна.
5.2. remove(Объект объекта) & removeElement(Объект объекта)
public boolean remove(Object o) {
return removeElement(o);
}
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
remove(Object object) фактически вызывает removeElement(Object object) внутри. Операция удаления сначала находит индекс объекта (так же, как remove(E) в ArrayList), а затем вызывает removeElementAt(i) (метод fastRemove() в ArrayList) для удаления.
Остальные операции удаления аналогичны ArrayList и здесь подробно не анализируются. Вообще говоря, в методе удаления Vector и ArrayList похожи.
6. Поточно-безопасный вектор?
Расширяя наше мышление, мы часто говорим, что Vector — это потокобезопасный список массивов, поэтому всегда ли он потокобезопасен? В StackOverFlow есть такой вопрос:
Is there any danger, if im using one Vector(java.util.Vector) on my server program when im accessing it from multiple threads only for reading? (myvector .size() .get() ...) For writing im using synchronized methods. Thank you.
Есть более подробный анализ ответа:
Каждый отдельный метод в Vector является потокобезопасным, потому что он украшен синхронизированными файлами . Но если вы столкнулись с некоторыми более сложными операциями, и несколько потоков должны полагаться на вектор для принятия связанных суждений, то в настоящее время он не является потокобезопасным.
if (vector.size() > 0) {
System.out.println(vector.get(0));
}
Как показано в приведенном выше коде, после того, как Vector определил, что size()>0, если другой поток одновременно очистит векторный объект, в это время возникнет исключение. Поэтому Vector не является потокобезопасным в случае составных операций.
Суммировать
Название этой статьи: Vector, причина в том, что если мы не знаем Vector подробно или в интервью, мы часто думаем, что Vector является потокобезопасным. Но на самом деле Vector является потокобезопасным только для каждой операции метода.
Подводя итог различиям с ArrayList:
- 1. Конструктор, ArrayList немного глубже, чем Vector.Длина массива по умолчанию для Vector равна 10, а создание является настройкой.
- 2. Метод расширения row(), ArrayList расширяется за счет битовых операций, а Vector за счет коэффициента роста (устанавливается create, если слишком пусто, то удвоится)
- 3. Вызов векторных методов потокобезопасен.
- 4. Переменные-члены отличаются
Использованная литература:
- http://www.cnblogs.com/skywang12345/p/3308833.html
- https://juejin.cn/post/6844903601928667149
- https://stackoverflow.com/questions/23246059/java-vector-thread-safety?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
- https://blog.csdn.net/ns_code/article/details/35793865