Difference between ArrayList and LinkedList

ArrayList and Vector use the implementation of arrays. It can be considered that ArrayList or Vector encapsulates operations on internal arrays, such as adding, deleting, inserting new elements to the array, or expanding and redirecting data.

 

LinkedList uses a circular doubly linked list data structure. Compared with the array-based ArrayList, these are two very different implementation techniques, which also determines that they will be suitable for completely different work scenarios.

 

LinkedList linked list is formed by connecting a series of entries. A table entry always contains 3 parts: the element content, the predecessor table and the successor table.

The following figure shows the connection relationship between the entries of a LinkedList containing 3 elements. In the JDK implementation, no matter whether the LikedList is empty or not, there is a header entry inside the linked list, which represents both the beginning and the end of the linked list. The trailing entry of the entry header is the first element in the linked list, and the leading entry of the entry header is the last element in the linked list

Let's compare the differences between ArrayList and LinkedList by adding and deleting elements as an example:

1

(1) Add elements to the end of the list:

 

The code to add an element to the end of the queue in an ArrayList is as follows:

 

public boolean add(E e){
  ensureCapacity(size+1);//确保内部数组有足够的空间
  elementData[size++]=e;//将元素加入到数组的末尾,完成添加
  return true;      
}

 

The performance of the add() method in ArrayList is determined by the ensureCapacity() method. The implementation of ensureCapacity() is as follows:

 

public vod ensureCapacity(int minCapacity){
 modCount++;
 int oldCapacity=elementData.length;
 if(minCapacity>oldCapacity){    //如果数组容量不足,进行扩容
     Object[] oldData=elementData;
     int newCapacity=(oldCapacity*3)/2+1;  //扩容到原始容量的1.5倍
     if(newCapacitty<minCapacity)   //如果新容量小于最小需要的容量,则使用最小
                                                   //需要的容量大小
        newCapacity=minCapacity ;  //进行扩容的数组复制
        elementData=Arrays.copyof(elementData,newCapacity);
 }
}

 

It can be seen that as long as the current capacity of the ArrayList is large enough, the efficiency of the add() operation is very high. Expansion is only required when the ArrayList's capacity requirement exceeds the current array size. During the expansion process, a large number of array copy operations will be performed. When the array is copied, the System.arraycopy() method will eventually be called, so the efficiency of the add() operation is still quite high.

 

The add() operation of LinkedList is implemented as follows, which also adds any element to the end of the queue:

 

public boolean add(E e){
  addBefore(e,header);//将元素增加到header的前面
  return true;
}

 

The addBefore() method is implemented as follows:

 

private Entry<E> addBefore(E e,Entry<E> entry){
    Entry<E> newEntry = new Entry<E>(e,entry,entry.previous);
    newEntry.provious.next=newEntry;
    newEntry.next.previous=newEntry;
    size++;
    modCount++;
    return newEntry;
}

 

It can be seen that LinkeList does not need to maintain the size of the capacity because it uses the structure of the linked list. From this point of view, it has certain performance advantages over ArrayList. However, each time the element is added, a new Entry object needs to be created and more assignment operations are performed. In frequent system calls, it will have a certain impact on performance.

 

1

(2) Add elements to any position in the list

 

In addition to providing elements to the end of the List, the List interface also provides a method for inserting elements at any position: void add(int index,E element);

 

Due to different implementations, there are certain performance differences between ArrayList and LinkedList in this method. Since ArrayList is implemented based on arrays, and the array is a continuous memory space, if you insert an element at any position in the array, it will inevitably lead to after the position. All elements of the need to be rearranged, so its efficiency will be relatively low.

 

The following code is the implementation in ArrayList:

 

public void add(int index,E element){
  if(index>size||index<0)
     throw new IndexOutOfBoundsException(
       "Index:"+index+",size: "+size);
        ensureCapacity(size+1);
        System.arraycopy(elementData,index,elementData,index+1,size-index);
        elementData[index] = element;
        size++;
}

 

You can see that for each insert operation, an array copy is performed. And this operation does not exist when adding elements to the end of the List, and a large number of array reorganization operations will lead to low system performance. And the higher the position of the inserted element in the List, the greater the cost of array reorganization.

 

And LinkedList shows the advantage at this point:

 

public void add(int index,E element){
  addBefore(element,(index==size?header:entry(index)));
}

 

It can be seen that for LinkedList, inserting data at the end of the List is the same as inserting data at any position, and the performance of the insertion method will not be degraded because the insertion position is earlier.

 

1

(3) Delete any element at any position

 

For element deletion, the List interface provides methods to delete elements at any position:

 

public E remove(int index);

 

For ArrayList, the remove() method is the same as the add() method. After removing an element from any position, the array is reorganized. The implementation of ArrayList is as follows:

 

public E remove(int index){
  RangeCheck(index);
  modCount++;
  E oldValue=(E) elementData[index];
 int numMoved=size-index-1;
 if(numMoved>0)
    System.arraycopy(elementData,index+1,elementData,index,numMoved);
    elementData[--size]=null;
    return oldValue;
}

 

It can be seen that after each valid element deletion operation of the ArrayList, the array must be reorganized. And the earlier the deletion is, the more expensive the array reorganization is.

 

public E remove(int index){
 return remove(entry(index));         
}
private Entry<E> entry(int index){
 if(index<0 || index>=size)
     throw new IndexOutBoundsException("Index:"+index+",size:"+size);
     Entry<E> e= header;
     if(index<(size>>1)){//要删除的元素位于前半段
        for(int i=0;i<=index;i++)
            e=e.next;
    }else{
        for(int i=size;i>index;i--)
            e=e.previous;
    }
        return e;
}

 

In the implementation of LinkedList, the first step is to find the element to be deleted by looping. If the position to be deleted is in the first half of the List, search from front to back; if the position is in the second half, search from back to front. Therefore, it is very efficient to delete the elements at the front or the back; but to remove the elements in the middle of the List, it takes almost half of the List to traverse, and the efficiency is very low when the List has a large number of elements.

 

1

(4) Capacity parameters

 

The capacity parameter is a specific performance parameter for array-based Lists such as ArrayList and Vector. It represents the initialized array size. When the number of elements stored in the ArrayList exceeds its existing size. It will expand, and the expansion of the array will cause a memory copy of the entire array. Therefore, a reasonable array size helps reduce the number of times of array expansion, thereby improving system performance.

 

public  ArrayList(){
 this(10);  
}
public ArrayList (int initialCapacity){
  super();
  if(initialCapacity<0)
      throw new IllegalArgumentException("Illegal Capacity:"+initialCapacity)
     this.elementData=new Object[initialCapacity];
}

 

ArrayList provides a constructor that can specify the initial array size:

 

public ArrayList(int initialCapacity)

 

Now take the construction of a List with 1 million elements as an example. When the default initialization size is used, the relative time consumed is about 125ms. When the array size is directly specified as 1 million, the construction of the same ArrayList takes only 16ms.

 

1

(5) Traverse the list

 

Traversing a list operation is one of the most commonly used list operations. After JDK1.5, there are at least three commonly used list traversal methods: forEach operation, iterator and for loop.

 

String tmp;
long start=System.currentTimeMills();    //ForEach 
for(String s:list){
   tmp=s;
}
System.out.println("foreach spend:"+(System.currentTimeMills()-start));
start = System.currentTimeMills();
for(Iterator<String> it=list.iterator();it.hasNext();){    
  tmp=it.next();
}
System.out.println("Iterator spend;"+(System.currentTimeMills()-start));
start=System.currentTimeMills();
int size=;list.size();
for(int i=0;i<size;i++){                     
   tmp=list.get(i);
}
System.out.println("for spend;"+(System.currentTimeMills()-start));

 

Construct an ArrayList with 1 million data and an equivalent LinkedList, use the above code to test, you can see that the simplest ForEach loop does not have good performance, and the overall performance is not as good as ordinary iterators, but for loops When traversing the list with random access, the ArrayList entries are fine, but the LinkedList's performance is unacceptable, and there is no way to even wait for the program to end. This is because when random access to LinkedList is performed, a list traversal operation is always performed. Very poor performance and should be avoided.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325479459&siteId=291194637