1. Анализ исходного кода ArrayList (JDK7)
ArrayList поддерживает динамический массив объектов внутри. Динамическое добавление и удаление ArrayList - это динамическое добавление и удаление этой пары групп.
1. Строительство и инициализация ArrayList
Arraylist apitlip appitation aptitic intatic int int default_capacity = 10; // массив пустых объектов по умолчанию, используемый для определения пустого ArrayListPrivate Статический конечный объект [] empty_elementdata = {}; // ArrayList сохраняет массив объектов Private Transient объект [] elementData; // количество элементов в ArrameList Private Size;ArrayList Constructor:
Нет конструктора параметров: то есть построить пустой объект []
public arraylist () {super (); this.elementData = empty_elementData;} Укажите конструкцию размера емкости:
public ArrayList (int initialCapacity) {super (); if (initialCapacity <0) бросить новую allosalargumentException («Незаконная емкость:»+ initialCapacity); this.elementData = new Object [initialCapacity];} Укажите структуру сбора, которая реализует интерфейс коллекции:
public arraylist (collection <? Extends e> c) {elementData = c.toarray (); size = elementData.length; // c.toarray может (неправильно) не возвращать объект [] (см. 6260652) if (elementData.getClass ()! = Object []. Class) elementData = Arrays.copyof (elementData, size, object []. Class);};};};};};Это также объясняет роль сбора и причину, по которой Java-Collection-Framwork разрабатывает интерфейс коллекции вместо непосредственного использования списка, SET и других интерфейсов.
2. Механизм распределения емкости ArrayList
Крапа емкости для ArrayList: емкость ArrayList является верхним пределом, а теории позволяют распределять Integer.max_Value - емкость 8 размеров. Тем не менее, сколько может быть выделено
Private Static Final int max_array_size = integer.max_value - 8;
Расширить громкость при вызове метода добавления
public boolean add (e e) {EncureCapacityInternal (размер + 1); // Приращивание ModCount !! elementData [size ++] = E; вернуть истину; }Метод EncureCapacityEnternal (Int) фактически определяет минимальный размер расширения.
Private void EnsureCapacityInternal (int mincapacity) {if (elementData == empty_elementData) {mincapacity = math.max (default_capacity, mincapacity); } обеспечить explicitcapacity (mincapacity); } private void inseexplicitcapacity (int mincapacity) {modcount ++; // переполненный код if (mincapacity - elementdata.length> 0) растут (mincapacity); } О MODCOUNT: MODCOUNT определяется в абстрактном классе AbstractList. Комментарии исходного кода в основном объясняет его использование: при использовании итератора для Traverse он используется для проверки того, имеют ли элементы в списке структурные изменения (количество элементов списка изменилось). Он используется в основном в многопоточной среде, чтобы предотвратить итерационную итерационную систему, а другой поток изменяет структуру этого списка.
Метод роста является реальным методом расширения
private void grow (int mincapacity) {// переполненный код int oldCapacity = elementData.length; int newCapacity = OldCapacity + (OldCapacity >> 1); if (newcapacity - mincapacity <0) newcapacity = mincapacity; if (newcapacity - max_array_size> 0) newcapacity = gugecapacity (mincapacity); // mincapacity обычно близка к размеру, так что это победа: elementData = arrays.copyof (elementdata, newcapacity); }Существует также метод HugeCapacity для того, сколько емкости расширяется
Private Static Int Hugecapacity (int mincapacity) {if (mincapacity <0) // переполнить новый OutofmemoryError (); return (mincapacity> max_array_size)? Integer.max_value: max_array_size; } Суммировать:
Каждое расширение сопровождается копией массива, поэтому обеспечение правильной мощности за раз улучшит производительность.
Следующий рисунок - весь процесс расширения, который я суммировал:
3. ArrayList Iterator
Есть два основных итератора ArrayList и Listitr, но ArraylistsPliterator также добавлен в JDK1.8. Давайте изучим анализ исходного кода ITR и ListITR соответственно.
(1) ITR: может только вернуться назад
Частный класс ITR реализует итератор <e> {int cursor; // индекс следующего элемента для возврата int lastret = -1; // Индекс последнего элемента возвращался; -1 Если нет такого // weadmodcount -это копия modcount int weddcount = modcount; public boolean hasnext () {return cursor! = size; } @Suppresswarnings ("unchecked") public e next () {checkforComodification (); // Записать текущую позицию int i = cursor; if (i> = size) бросить новый noshelementexception (); Object [] elementData = ArrayList.This.ElementData; if (i> = elementdata.length) бросить новый concurrentmodificationexception (); // положение следующего элемента cursor = i + 1; вернуть (e) elementData [lastret = i]; } // Используйте метод удаления итератора public void remove () {if (lastret <0) бросить new allogalStateException (); checkforComodification (); Попробуйте {// Обратите внимание, как внутренний класс вызывает внешний класс ArrayList.his.remove (lastret); // после удаления вам необходимо повторно настроить положение каждого курсора Pointer = lastret; lastret = -1; weddcount = modcount; } catch (indexoutOfBoundSexception ex) {бросить новый comproundModificationException (); / }} Из исходного кода видно, что итератор ITR является прямом итератором, который предоставляет следующий метод для получения элементов в ArrayList.
CheckforComodification-это ошибочный механизм обнаружения ошибок в фрам-образе Java-Collection. Работа на том же наборе в многопоточной среде может вызвать ошибочный механизм и выбросить исключение CondurentModificationException.
Итератор ITR определяет копию ModCount Record ModCount WordmodCount. Когда ArrayList выполняет операции для изменения структуры, таких как добавление, удаление и очистки методов, значение ModCount изменится.
Через исходный код ITR можно увидеть, что вызов следующего и удаление методов вызовет проверку сбоя. В настоящее время, если исключение происходит, когда другие потоки выполняют операции, которые изменяют установленную структуру при прохождении набора.
(2) Listitr: поддерживает вперед и назад. Давайте посмотрим на исходный код списка:
Private Class Listitr Extens Itr реализует ListIterator <e> {listitr (int index) {super (); cursor = index; } public boolean hasprevious () {return cursor! = 0; } public int nextIndex () {return cursor; } public int int indIndex () {return cursor - 1; } @Suppresswarnings ("unchecked") public e предыдущий () {checkforComodification (); // положение предыдущего элемента ArrayList int i = курсор - 1; if (i <0) бросить новый noshelementexception (); Object [] elementData = ArrayList.This.ElementData; if (i> = elementdata.length) бросить новый concurrentmodificationexception (); курсор = i; вернуть (e) elementData [lastret = i]; } // Метод установления добавляется к этому итераторному набору void (e E) {if (lastret <0) бросить new allogalstateexception (); checkforComodification (); try {arraylist.this.set (lastret, e); } catch (indexoutOfBoundSexception ex) {бросить новый comproundModificationException (); }} // Этот итератор добавляет метод добавления public void add (e e) {checkforComodification (); попробуйте {int i = cursor; Arraylist.his.add (i, e); // Заметить положение указателя cursor = i + 1; lastret = -1; weddcount = modcount; } catch (indexoutOfBoundSexception ex) {бросить новый comproundModificationException (); }}}Реализация ListITR в основном такая же, как ITR, добавляя методы, которые можно пройти ранее, а также добавлять и устанавливать методы.
(3) Используйте CopeonWritearRaylist в java.util.concurrent для решения проблемы быстрого подхода
CopyonWritearRaylist-это безопасность. Для получения подробной информации, давайте посмотрим на его метод добавить исходный код:
public boolean add (e e) {final Reentrantlock lock = this.lock; lock.lock (); try {object [] elements = getarray (); int len = elements.length; Object [] newElements = arrays.copyof (elements, len + 1); Новые размеры [len] = e; Setarray (новые выборы); вернуть истину; } наконец {lock.unlock (); }} CopyonWritearRaylist - это ArrayList, скопированный на записи. При запуске операции написания данных Arrays.copyof - это новый массив, который не повлияет на операцию чтения.
Эта стоимость состоит в том, чтобы потерять память и вызвать проблемы с производительностью. Когда написан CopeonWritearRaylist, в памяти генерируется объект копирования, а исходный объект все еще существует.
CopyonWritearRaylist не может гарантировать согласованность данных в режиме реального времени, он может гарантировать только согласованность результатов. Подходит для сценариев, таких как кэш, когда читает больше и больше писать и писать меньше в одновременных ситуациях.
(4) Другие методы исходный код ArrayList:
Частный метод BatchRemove (Collection <?> C, логический комплемент), то есть операция удаления партии
Private Boolean BatchRemove (Collection <?> C, логический комплемент) {// Причина использования окончания упоминается ниже Final Object [] elementData = this.elementData; int r = 0, w = 0; логический модифицированный = false; try {// спокойствие через элементы в списке и проверить (; r <size; r ++) if (c.contains (elementdata [r]) == комплемент) elementdata [w ++] = elementdata [r]; } Наконец {// Если исключение происходит в TRY, убедитесь, что данные о данных и выполните следующую операцию копирования, если (r! = size) {System.ArrayCopy (elementData, R, ElementData, W, Size - R); w += size - r; } // Очистить неиспользованные элементы и уведомлять GC для переработки if (w! = Size) {// ясно, чтобы GC выполнял свою работу для (int i = w; i <size; i ++) elementdata [i] = null; modcount += size - w; размер = w; модифицированный = true; }} return modified; } Переменная, измененная окончательной, относится к той же ссылке, чтобы поддерживать согласованность данных позже.
В этом методе, когда вы хотите сохранить элементы в сборе C, значение дополнения верно; Если вы хотите удалить элементы в C, значение дополнения является ложным. Это становится методами удержания и удаления соответственно.
Смена: обмениваться двумя позициями в ArrayList
2. Анализ исходного кода LinkedList (JDK7)
LinkedList - это связанный список. По сравнению с таблицей заказов, связанного списка не нужно использовать непрерывные блоки памяти для хранения данных. Уменьшает проблему перемещения элементов, вызванных изменением структуры контейнера, и последовательный доступ относительно эффективен.
1. Определение узла
LinkedList в JDK - это двунаправленный связанный список, каждый узел хранит информацию о предыдущем узле и следующем узле соответственно. Его определение следующее:
Частный статический класс узел <e> {e item; Узел <e> Далее; Узел <e> prev; Узел <e> (Node <e> prev, e element, node <e> Далее) {this.item = element; this.next = Далее; this.prev = prev; }}2. Строительство и инициализация LinkedList
Участник: 3 переменные члена поддерживаются в LinkedList для записи количества узлов в связанном списке, предшественника и преемника узлов
Transient int size = 0; переходной узел <e> сначала; переходной узел <e> последний;
Конструктор: конструктор по умолчанию - построить пустой LinkedList
public linkedList () {}Или конструкция на основе других контейнеров, а затем мы напишем конструктор, чтобы сформировать упорядоченный список ссылок.
public LinkedList (Collection <? Extends E> C) {this (); addall (c);}Вот немного больше. Для разницы между общим модификатором? Super T и расширяет T, см. Эта статья о разнице между Super T и расширяет T в дженериках.
3. Структурная работа LinkedList
Метод вставки заголовка: то есть вставьте элемент в заголовок связанного списка
private void linkfirst (e e) {final node <e> f = First; Окончательный узел <e> newnode = new Node <> (null, e, f); First = newNode; // судить, является ли это пустым связанным списком, если (f == null) Last = newNode; else f.prev = newnode; размер ++; modcount ++; } Метод вставки хвоста: то есть вставьте элемент в конце связанного списка
void linklast (e e) {final node <e> l = last; Окончательный узел <e> newnode = new Node <> (l, e, null); последний = newNode; if (l == null) first = newnode; else l.next = newnode; размер ++; modcount ++; } Перед вставкой в текущий узел: найти передний привод текущего узла
void linkbefore (e e, node <e> succ) {// определить, не является ли узел, конечно, конечно окончательный узел <e> pred = succ.prev; Окончательный узел <e> newnode = new Node <> (Pred, E, SUCC); succ.prev = newnode; // определить, является ли текущий узел первым узлом, если (Pred == null) First = newNode; else Pred.next = newnode; размер ++; modcount ++; } Метод удаления заголовка: удалить первый узел связанного списка
private e unlinkfirst (node <e> f) {// assert f == First && f! = null; Final E Element = F.Item; Окончательный узел <e> next = f.next; F.Item = null; f.next = null; // Помогите GC First = Next; if (next == null) последний = null; else next.prev = null; размер--; modcount ++; возвратный элемент; } Метод удаления хвоста: удалить последний узел связанного списка
private e unlinklast (node <e> l) {// убедитесь, что l == last и l! = null final e element = l.item; Окончательный узел <e> prev = l.prev; L.Item = null; l.prev = null; // Помогите GC LAST = PREV; if (prev == null) First = null; else prev.next = null; размер--; modcount ++; возвратный элемент; }4. Поддерживайте согласованность между интерфейсом списка и Deque
Интерфейс списка позволяет использовать подписки для реализации случайного доступа к контейнерам, и легко реализовать случайный доступ к таким массивам. Для связанных списков JDK также логически использует количество узлов в связанных списках, чтобы дать реализацию случайного доступа
Узел <e> node (int index) {// Убедитесь, что правильность индекса if (index <(size >> 1)) {node <e> x = first; для (int i = 0; i <index; i ++) x = x.next; возврат x; } else {node <e> x = last; для (int i = size-1; i> index; i--) x = x.prev; возврат x; }} Индекс является подсчетом первой половины, поиск с самого начала. Индекс принадлежит подсчету второй половины и ищет с конца. В полной мере используйте характеристики двусторонних связанных списков.
Следовательно, добавить (int index, t t), get (int), set (int) и другие методы могут быть легко реализованы.
LinkedList реализует интерфейс Deque, то есть LinkedList реализует метод двойных контейнеров в очереди. Вот некоторое резюме API.
5. LinkedList Traversal
Поскольку LinkedList-это двусторонний список связанного списка, вы можете естественно пройти его туда-сюда. Как и ArrayList, LinkedList также имеет проблемы с ошибкой, когда речь заходит о многопоточной операции контейнера.
Проблема неудачи была объяснена в предыдущей статье, поэтому я не буду говорить об этом здесь.
Что касается итераторов, LinkedList имеет двунаправленный итератор ListIterator и обратный итератор с нисходом. Все очень просты. Исходный код не анализируется
Если вы пересекаете элементы, стоимость случайного доступа относительно высока.
3. LinkedList, Arraylist, Vector Resding
1. LinkedList и ArrayList
ArrayList реализует структуру данных на основе динамических массивов, а LinkedList основан на структуре данных, основанной на связанном списке.
Для случайного доступа к Get и Set, ArrayList чувствует себя лучше, чем LinkedList, потому что LinkedList перемещает указатель.
Для новых операций с удалением добавления и удаления, LinedList имеет лучшее преимущество, поскольку ArrayList должен перемещать данные. Это зависит от реальной ситуации. Если вставлен или удален только один кусок данных, скорость ArrayList лучше, чем в LinkedList. Однако, если данные вставляются случайным образом в партиях, скорость LinkedList намного лучше, чем в ArrayList. Поскольку каждый раз, когда ArrayList вставляет данные, необходимо перемещать точку вставки и все данные после этого.
2. Arraylist и Vector
Вектор является синхронным, поэтому он также безопасен для потока, в то время как ArrayList-это нить-асин, что не является безопасным. Если коэффициенты безопасности потока не учитываются, ArrayList, как правило, более эффективен.
Если количество элементов в наборе больше, чем длина текущего наборного массива, скорость роста вектора составляет 100% от текущей длины массива, а темпы роста массива составляет 50% от текущей длины массива. При использовании данных с относительно большими объемами данных в наборе, использование Vector имеет определенные преимущества.
Если вы ищете данные в указанном месте, время, используемое Vector и ArrayList, одинаково, оба 0 (1), и в настоящее время вы можете использовать Vector и ArrayList. Если время, потраченное на перемещение данных в указанном месте, составляет 0 (ni) n, что является общей длиной, вам следует рассмотреть возможность использования Linklist, поскольку требуется 0 (1), чтобы переместить данные в указанном месте, а время, потраченное на запрос данных в указанном месте, составляет 0 (i).
ArrayList и Vector используют массивы для хранения данных. Количество элементов в этом массиве больше, чем фактические сохраненные данные для добавления и вставки элементов. Оба разрешают прямые элементы индекса серийного числа. Тем не менее, вставка данных должна быть разработана для перемещения элементов массива и других операций памяти, поэтому данные индекса являются быстрыми и медленными для вставки данных. Vector использует синхронизированный метод (Safe Thread), поэтому производительность хуже, чем ArrayList. LinkedList использует двунаправленный связанный список для хранения данных. Индексирование данных в соответствии с серийным номером требует вперед или обратно, но при вставке данных записываются только передние и задние элементы этого элемента, поэтому вставка нескольких градусов происходит быстрее!