ArrayList срещу LinkedList срещу Vector

Наемането на работа в ролята на софтуерно инженерство е много по-лесно, отколкото хората мислят. Но в някои случаи по-голямата част от моите хора с дългогодишен опит в тази експертиза не успяха да кандидатстват. „Какъв е проблемът?“ Въпросът се надува в главата ми.

Наслаждавам се на времето си като роля на софтуерното инженерство, още повече обичам това, което правя с кода си през последните 6 години. През това време имах невероятни преживявания, интервюирайки много кандидати за софтуерно инженерство. Във всеки процес на интервю, не очаквам кандидатите да са добре обосновани в теми като добри принципи за проектиране на софтуер, архитектура на мащабируем код и тестване, но също така и техните познания в основата на структурата на данните и алгоритъма в софтуерното инженерство. Бях толкова тъжна, когато много кандидати не разбираха основното.

Освен бърз ученик, който може да научи нови рамки, инструменти или дори езици за програмиране за кратък период от време, доброто разбиране на структурата на данните е едно от основните знания, които трябва да имате, ако искате да продължите кариера като софтуерен инженер.

Структурата на данните е особен начин за съхраняване и организиране на информация в компютър, така че тя да може да бъде извлечена и използвана най-продуктивно. Различните видове структури от данни са предназначени за различни видове приложения, а някои са тясно специализирани за конкретни задачи. - Уикипедия

Защо структурата на данните се превръща в едно от важните знания, които трябва да имате? Структурата на данните и алгоритъмът предоставят набор от техники за ефективно управление на данните. Ако кандидатите не знаят за структурата и алгоритъма на данните, те може да не са в състояние да напишат ефективен код за обработка на данните. Знанието как структурата на данните съхранява своите данни, как се изпълняват и кога да се използва с предварително определен набор от техники, ще подобри времето, необходимо за разрешаване на проблема. Един от въпросите, на които кандидатите често не отговарят, е

Кой по-добър, ArrayList, LinkedList или Vector?

списък

Преди да отговорите на въпроса, е добре да знаете какво е Списък. Списъкът е един от абстрактния тип данни (ADT), който съхранява елементите си в поредица и ни позволява да добавяме, премахваме и осъществяваме достъп до неговите елементи с помощта на индекс. Масивът е една от структурите на данни, която реализира списъка ADT. В Array можем да добавяме, изтриваме и получаваме елемент с помощта на индекс. ArrayList, LinkedList и Vector са друг пример за структури от данни, които внедряват ADT List. Докато Array има статичен размер, след като декларира, ArrayList, LinkedList и Vector имат динамичен размер (промяна на размера). Ако масивът е деклариран с дължина 7, тогава той ще има дължина 7 до края на обхвата му. Докато ArrayList, LinkedList и Vector могат да съхраняват данни, доколкото е възможно, където ограничението е общата свободна памет.

Java Collection Framework. Източник: Wikipedia

В Java Collection Framework, списъкът ADT се реализира като интерфейс, наречен списък, а ArrayList, LinkedList & Vector са конкретният клас, който реализира интерфейса на List. Тъй като тези конкретни класове реализират един и същ интерфейс, тези класове трябва да имат една и съща функционалност, тъй като за всички нео-абстрактни класове, които реализират интерфейс, се изисква да реализират всички абстрактни методи, декларирани в интерфейса.

Как се съхраняват данни

Основната разлика на трите структури от данни по-горе е начинът, по който те съхраняват своите данни, което причинява различна производителност за различните операции. В Java (и също използван в Kotlin), ArrayList и Vector използват Array, за да съхраняват своите елементи, докато LinkedList съхранява елементите си в двойно свързан списък.

В компютърните науки двойно свързан списък е свързана структура от данни, която се състои от набор от последователно свързани записи, наречени възли. Всеки възел съдържа две полета, наречени връзки, които са препратки към предишния и следващия възел в последователността на възлите. - Уикипедия
ArrayList (отгоре) срещу LinkedList (отдолу). Източник https://dzone.com/storage/temp/895349-arraylist-linkedlistt.png

В LinkedList първият възел може да знае втория възел. Вторият възел може да познава първия и третия възел. Третият възел може да знае втория и четвъртия възел. И така нататък, един възел може да знае предишния възел и следващия възел. Специално за първия възел в LinkedList няма да има предишен възел и също така последният възел в LinkedList няма да има следващ възел.

Ето фрагмента на кода как ArrayList съхранява своите елементи в Array.

/ **
 * Буферът на масива, в който са елементите на ArrayList
 * съхранява се. Капацитетът на ArrayList е дължината на това
 * буфер на масив Всеки празен ArrayList с elementData ==
 * DEFAULTCAPACITY_EMPTY_ELEMENTDATA ще бъде разширен до
 * DEFAULT_CAPACITY при добавяне на първия елемент.
 * /
преходен обект [] elementData; // неприватна за опростяване на вложени
                                // достъп до клас

И ето как Vector съхранява своите елементи и в Array.

/ **
 * Буферът на масива, в който са компонентите на вектора
 * съхранява се. Капацитетът на вектора е дължината на този масив
 * буфер и е поне достатъчно голям, за да съдържа всички вектори
 * елементи.
 *
 * 

Всякакви елементи от масива след последния елемент в Vector  * са нищожни.  *  * @сериен  * / защитен обект [] elementData;

В горния фрагмент на кода можем да видим, че ArrayList и Vector съхраняват елементите си в променлива с име elementDatawith Object [] като свой тип. Защо обект []? Object е суперклас на всички класове в Java. Като дефинира elementData като Object [], тогава elementData може да съхранява различни видове елементи, това може да бъде String, Integer, Double, Float или потребителски клас като Pair, TextView и така нататък.

Защо ArrayList и Vector използват Array за съхраняване на своите данни? Както споменахме по-рано, въпреки че ArrayList и Vector използват масиви за съхранение на своите данни, ArrayList и Vector имат възможност да имат динамични размери (с възможност за промяна на размера). Когато масивът, използван в ArrayList и Vector, е пълен, те могат да "растат", за да увеличат капацитета си. ArrayList и Vector увеличават капацитета си, като създават нов масив с размер, по-голям от предишния размер, след което копират всички елементи от стария масив в новия масив, като използват команда Arrays.copyOf. Ето кодов фрагмент на програмата в ArrayList за увеличаване на капацитета му (източник: ArrayList.java).

/ **
  * Увеличава капацитета, за да гарантира, че може да побере поне
  * брой елементи, определени от аргумента за минимален капацитет.
  *
  * @param minCapacity желания минимален капацитет
  * /
  частно увеличение на празнотата (int minCapacity) {
      // код за преливане
      int oldCapacity = elementData.length;
      int newCapacity = oldCapacity + (oldCapacity >> 1);
      ако (newCapacity - minCapacity <0)
          newCapacity = minCapacity;
      ако (newCapacity - MAX_ARRAY_SIZE> 0)
          newCapacity = огромен капацитет (minCapacity);
      // minCapacity обикновено е близо до размера, така че това е печалба:
      elementData = Arrays.copyOf (elementData, newCapacity);
  }

И ето как Vector увеличава капацитета си (източник: Vector.java)

частно увеличение на празнотата (int minCapacity) {
    // код за преливане
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((капацитетПовишение> 0)?
                                     увеличаване на капацитета: oldCapacity);
    ако (newCapacity - minCapacity <0)
        newCapacity = minCapacity;
    ако (newCapacity - MAX_ARRAY_SIZE> 0)
        newCapacity = огромен капацитет (minCapacity);
    elementData = Arrays.copyOf (elementData, newCapacity);
}

Реализацията на вектора е почти идентична с ArrayList и единствената разлика е, че всички операции в Vector са синхронизирани, което прави всеки метод, който докосва съдържанието на Vector, да е безопасен с нишки.

И така, кой по-добър, ArrayList, LinkedList или Vector? За да отговорите на този въпрос, можем да сравним производителността за общи операции в ArrayList и LinkedList. Тъй като реализацията на ArrayList и Vector е почти идентична, тяхната производителност също ще бъде почти еднаква.

Сравнение на производителността между ArrayList и LinkedList

От таблицата по-горе можем да видим, че няма сребърен куршум за всички операции. ArrayList е по-добър от LinkedList при случаен достъп (get). ArrayList по-добър от LinkedList, защото ArrayList съхранява елементите си в Array, докато LinkedList съхранява елементите си в свързания възел. Например, за да получат 5-ия елемент, ArrayList и Vector могат директно да получат неговия елемент, като използват индекс, тъй като ArrayList и Vector по същество е масив, докато LinkedList трябва да бъде повторен, за да открие петия елемент, защото в LinkedList можем да знаем само първия и само последен елемент

За insertFirst и deleteFirst LinkedList е по-добър от ArrayList. ArrayList изисква големи усилия за добавяне на елемент в началото (първи индекс) или изтриване на първия елемент, защото ArrayList трябва да измести следните елементи след добавяне или изтриване. Докато LinkedList трябва само да замени / зададе своите указатели. Що се отнася до другите функции, и ArrayList, и LinkedList имат сравнително еднаква производителност.

И така, кой по-добър, ArrayList, LinkedList или Vector? Зависи от вашите нужди. Ако използвате произволен достъп (получите) по-често, тогава ArrayList и Vector са добър избор. Изберете Vector, ако се нуждаете от колекция, безопасна за конци. Но ако често правите допълнения или изтривания на първа позиция (индекс), LinkedList е по-добър избор.

Има още един ADT, който често се използва при разработване на приложения като Set, Map и PriorityQueue. За да знаете как съхраняват данните си, как работят и кога да ги използвате, непрекъснато оставайте в тон с моя носител.