The difference between ArrayList and LinkedList?

image

As can be seen from the figure, ArrayList and LinkedList are both implementation classes of the List interface, so they all implement all the unimplemented methods of List, but the implementation is different. (From this, you can see the benefits of interface-oriented, for different There are different implementations for the needs of the list!), and the List interface inherits the Collection interface, and the Collection interface inherits the Iterable interface, so it can be seen that the List has the characteristics of the Collection and Iterable interfaces at the same time.

ArrayList

ArrayList is an implementation of the variable array of the List interface. All optional list operations are implemented, and all elements including null are allowed. In addition to implementing the List interface, this class also provides some methods to manipulate the size of the array used to store the list.
Each ArrayList instance has a capacity, which refers to the size of the array used to store the elements of the list. It is always at least equal to the size of the list. As you continue to add elements to the ArrayList, its capacity automatically grows. Automatic growth will bring data to a new copy of the new array, so if the amount of data can be predicted, you can specify its capacity when constructing the ArrayList. Before adding a large number of elements, the application can also use the ensureCapacity operation to increase the capacity of the ArrayList instance, which can reduce the number of incremental reallocations.
Note that this implementation is not synchronous . If multiple threads access an ArrayList instance at the same time, and at least one of them modifies the list structurally, then it must maintain external synchronization. This is usually achieved by synchronizing the objects used to encapsulate the list. But if no such object exists, the list needs to be "wrapped" with {@link Collections#synchronizedList Collections.synchronizedList}. This method is best done when the list object is created. Synchronous operation.

List list = Collections.synchronizedList(new ArrayList(...));

It is recommended to use ArrayList only in a single thread, and you can choose Vector or CopyOnWriteArrayList in multiple threads.

Expansion

An obvious feature of an array is that its capacity is fixed , and once the array is created, the capacity cannot be changed. So before adding a specified element to the array, the first thing to consider is whether its capacity is saturated.

If the elements in the array exceed its capacity during the subsequent addition operation, it must be expanded . Limited by the fixed capacity of the array, the essence of expansion is actually to create a new array with a larger capacity , and then copy the elements of the old array to the new array.

Here, take the adding operation of ArrayList as an example to look at the process of expanding the internal array of ArrayList.

public boolean add(E e) {// Key -> Before adding, verify the capacity 
	ensureCapacityInternal(size + 1);  
	
	// Modify the size and add the specified element at the end of the array 
	elementData[size++] = e; return true; 
}

It can be found that ArrayList will check the internal array capacity and selectively expand the array before adding operation . In ArrayList, the private method  ensuresCapacityInternal is  used to expand the array. Let's look at the specific implementation process:

  • The first step of the expansion operation is to determine whether the current ArrayList internal array is empty. If it is empty, the minimum capacity minCapacity is set to 10.

// The default capacity of the internal array private static final int DEFAULT_CAPACITY = 10;// The empty internal array private static final Object[] EMPTY_ELEMENTDATA = {};// Key -> minCapacity = seize+1, which means after the addition operation is performed , The number of elements in the array private void ensureCapacityInternal(int minCapacity) {// Determine whether the internal array is empty 
	if (elementData == EMPTY_ELEMENTDATA) {// Set the minimum capacity of the array (>=10) 
		minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 
	} 
	ensureExplicitCapacity(minCapacity); 
}
  • Then determine whether the addition operation will cause the capacity of the internal array to saturate.

private void ensureExplicitCapacity(int minCapacity) { 
	modCount++;	 
	// If the judgment result is true, it means that the next addition operation will cause the number of elements to exceed the array capacity 
	if (minCapacity-elementData.length> 0){ // The real expansion operation 
		grow( minCapacity); 
	} 
}
  • If the capacity of the array is insufficient, the expansion operation is carried out. There are two key points: the expansion formula , and the expansion operation is completed by copying elements from the old array to the new array.

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8; private void grow(int minCapacity) {	 
	int oldCapacity = elementData.length;	 
	// key -> capacity expansion formula 
	int newCapacity = oldCapacity + (oldCapacity >> 1);	 
	// for A series of judgments of the new capacity 
	if (newCapacity-minCapacity <0){ 
		newCapacity = minCapacity; 
	} if (newCapacity-MAX_ARRAY_SIZE> 0){ 
		newCapacity = hugeCapacity(minCapacity); 
	}		 
	// Key -> Copy the old array elements to the new array Go to 
	elementData = Arrays.copyOf(elementData, newCapacity); 
}private static int hugeCapacity(int minCapacity) {if (minCapacity <0){ throw new OutOfMemoryError(); 
	}			 
	return (minCapacity> MAX_ARRAY_SIZE)? Integer.MAX_VALUE: MAX_ARRAY_SIZE; 
}

关于 ArrayList 扩容操作,整个过程如下图:

image

LinkedList

image

  1. LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。

  2. LinkedList 实现 List 接口,能对它进行队列操作。

  3. LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。

  4. LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。

  5. LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。

  6. LinkedList 是非同步的。

image

如上图所示,LinkedList底层使用的双向链表结构,有一个头结点和一个尾结点,双向链表意味着我们可以从头开始正向遍历,或者是从尾开始逆向遍历,并且可以针对头部和尾部进行相应的操作。

private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;

Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

总结

1. ArrayList is a data structure based on a dynamic array, and LinkedList is a data structure based on a linked list.
2. For random access to get and set, ArrayList feels better than LinkedList, because LinkedList needs to move the pointer.
3. For the add and remove operations of adding and deleting, LinedList is more dominant, because ArrayList needs to move data.


Guess you like

Origin blog.51cto.com/15082402/2644370