Java collections: List summary

1 Overview

In the Java collection classes, the definition of the various data structures, according to the implementation of the interface can be divided into two categories: one is to achieve the Collection interface; the other is the realization Map interface , while List is a Collection of important sub-interfaces , ordered list represents.
Collection

  • List Features: It is ordered, repeatable 's collection, which is an index of.
  • List implementation class: ArrayList, LinkedList, Vector, Stack, etc.
    • ArrayList array is implemented based on, an array queue.
    • LinkedList is based on the realization of the list, is a two-way circulation list.
    • Vector-based array is implemented, it is a vector queue is thread safe to.
    • Stack is based on the array to achieve, is the stack, it inherits the Vector, is characteristic of FILO (last out)

2.ArrayList

ArrayList is an array of structures, allowing pull nulls achieved automatic expansion , not thread safe. In the source code through elementData of [] array to store data objects, dynamic memory expansion by certain rules;

    /**
     * Default initial capacity.
     * 默认初始化大小
     */
    private static final int DEFAULT_CAPACITY = 10;
    
    /*
    * Shared empty array instance used for default sized empty instances
    */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     * 存入Arraylist的元素数组缓存,Arraylist的存储大小与elementData的数组长度
     * 相同,在加入第一个元素时,任何空数组的缓存 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,将被扩容至DEFAULT_CAPACITY大小
     */	
     transient Object[] elementData
     // 当前数组大小,类型 int,没有使用 volatile 修饰,非线程安全的;
      private int size;

2. 1. New and expansion

The Add New () : is added to the array element divided into two steps:
 1. Whether determining capacity is needed, if needed, performs expansion operation;
 2. Direct assignment.

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

Analyzing expansion:
1. Arraylist determines whether to add the first value;
2. Analyzing the desired capacity is smaller than the existing buffer capacity;
3. By grow (minCapacity), to achieve expansion;

      private static int calculateCapacity(Object[] elementData, int minCapacity) {
			// 1.判断elementData是否为空,返回elementData和
			// DEFAULTCAPACITY_EMPTY_ELEMENTDATA的最大值
	        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
	            return Math.max(DEFAULT_CAPACITY, minCapacity);
	        }
	        // 非空
	        return minCapacity;
		}

   // 判断是否执行扩容,若期望的最小大小minCapacity > elementData.length 执行grow扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

Expansion of operations:

  1. Setting a new expansion of 1.5 times the original size of the capacity size. newCapacity = oldCapacity + (oldCapacity >> 1);
  2. If the new size of the expansion <capacity desired size, the desired capacity of the new expansion size = size;
  3. The lower limit is determined desired capacity of the vertical size, a lower limit of 0 and an upper limit Integer.MAX_VALUE;
  4. By Arrays.copyOf (), copy the original array expansion size;
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

to sum up:

  • No check on the new value can be null;
  • Expansion of 1.5 times the original size is the size of the capacity;
  • ArrayList maximum limit is Integer.MAX_VALUE, more than this value, JVM is no longer allocate space;
  • Expansion by the nature of the array to create a new copy of the object array a desired capacity;

2.2 Remove and empty

  1. Delete judgment:
  • You can remove null value;
  • Non-null value, determined by comparison of the equals method;
  1. Deletion: and expansion similar, operated by the same copy The arraycopy array (), the element will be removed after the advance;
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

Erase: traversing opposing null

  /**
     * Removes all of the elements from this list.  The list will
     * be empty after this call returns.
     */
    public void clear() {
        modCount++;
        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }

2. Thread Safety

Only when the ArrayList as shared variables, will have a security thread, when ArrayList is a local variable within the method, there is no thread safety issues.

ArrayList thread-safe nature of the problem, because ArrayList itself elementData, size, when modConut during various operations, are not locked, but is not visible type of these variables (volatile), so if multiple threads of these when the operating variable, value or may be covered.

We recommend the use of class comments Collections # synchronizedList to ensure thread safety, SynchronizedList is achieved by each method above plus lock, although to achieve a thread-safe, but the performance is greatly reduced.

3. LinkedList

LinkedList is a linked list structure, a non-thread-safe, with a characteristic list structure, frequently used in the queue. Node for dynamic storage, and front and rear link through a node of the list;
Node node:

    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;
        }
    }

3. 1. add and delete

Head and tail pointers LinkedList maintains two first and last: implements the list head insertion and deletion, insertion and deletion tail; its way to the operation of the node list.

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;
        /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

3. 2. Query

List query node is slow, need one by one query node, and did not use to traverse from start to finish in linkedList, whereas the use of binary thinking to find, first determine the list or index is located in the latter half of the first half, the decision is in the past after the front or from the rear;

    Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

3.3. LinkedList class Methods

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

When used as a LinkedList List, generally use add / get the method to push / get the object,
when used as a Queue, using only offer / poll / take the like.

4. Vector

Vector class implements a growable array of objects. Like an array, it contains components that can be accessed using an integer index. However, the size of a Vector can grow or shrink as needed to accommodate the operation to add or remove items after the creation of Vector. Vector is synchronized, can be used for multi-threading.

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • Vector inherited AbstractList, to achieve the List; therefore, it is a queue, support-related add, delete, modify, traverse functions.

  • Vector realized RandmoAccess interface, that provides random access function. RandmoAccess is being used in java implementation of List, provides quick access to the List. In Vector, we can quickly get that is through a number of elements of the element object; this is the fast random access.

Since the method and the Vector keywords are synchronized modified, can be synchronized threads are thread-safe;
basic operation and storage structures and ArrayList similar storage element array is through the bottom, through the expansion mechanism replication and arrays way to achieve add and remove elements;
Vector.add () Source:

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

Vector.remove () Source:

    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;
    }

5. List summary

The class name Implementation Mechanism Thread Safety Scenarios
ArrayList Array no Additions and deletions slow, fast query
LinkedList Doubly linked list no Additions and deletions fast, slow queries. Queue, list of scenes
Vector Array Yes Additions and deletions slow, fast queries, multi-threaded scenarios
Stack Array Inherited from Vector, is Scene in line with the characteristics of the stack: FILO (last out)

1). Array and ArrayList What is the difference? When the ArrayList instead of Array should it?

A: The difference between them:

Array can contain primitive types and object types, ArrayList only contains the object type.
Array size is fixed, the size of the ArrayList is dynamic.
ArrayList methods and provides more features, such as: addAll (), removeAll () , iterator () and the like.

2). ArrayList, LinkedList can remove elements traversed by for?
Can not, ① when used for general circulation, since each deletion will lead to forward a copy of the array elements will result in the deletion unclean;.. ② When using the enhanced for-each loop, it will be reported ConcurrentModificationException mistakes. Because the process is enhanced for loop is actually calling the iterator's next () method, when you call list.remove () method to delete, value +1 modCount, but this time the value of the iterator expectedModCount did not change, resulting in the implementation of next iterator next () method, expectedModCount = modCount, resulting in an error;!
should use iterators to delete, in Iterator.remove () method in the process of implementation, will be assigned to the latest modCount expectedModCount, so that in the next cycle, both will equal modCount and expectedModCount. You can normally remove elements.

3) The difference ArrayList and the Vector?
answer:

这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合,即存储在这两个集合中的元素位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引来取出某个元素,并且其中的数据是允许重复的,这是与 HashSet 之类的集合的最大不同处,HashSet 之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素。

ArrayList 与 Vector 的区别主要包括两个方面:

同步性:
Vector 是线程安全的,也就是说它的方法之间是线程同步(加了synchronized 关键字)的,而 ArrayList 是线程不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用 ArrayList,因为它不考虑线程安全的问题,所以效率会高一些;如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考虑和编写线程安全的代码。

数据增长:
ArrayList 与 Vector 都有一个初始的容量大小,当存储进它们里面的元素的个人超过了容量时,就需要增加 ArrayList 和 Vector 的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要去的一定的平衡。Vector 在数据满时(加载因子1)增长为原来的两倍(扩容增量:原容量的 2 倍),而 ArrayList 在数据量达到容量的一半时(加载因子 0.5)增长为原容量的 (0.5 倍 + 1) 个空间。

Reference
https://www.imooc.com/read/47#catalog
https://www.jianshu.com/p/939b8a672070
https://blog.csdn.net/zy1994hyq/article/details/82984523

Published 16 original articles · won praise 15 · views 30000 +

Guess you like

Origin blog.csdn.net/gcoder_/article/details/104291987