Java Collection - List Introduction and Source Parsing

Posted by Web For U on Sat, 11 May 2019 16:25:26 +0200

(Source version JDK 8)

Collection classes can be grouped into three types in a java.util package: Set, List, Map.

JAVA Collection Relationships (sketch)

(Picture Source Network)

Both List and Set collections inherit the Collection interface and are the top-level interfaces for List and Set, including the following methods:

List Collection

A List is an ordered collection (also known as a sequence) where you can control where each element is inserted and access the elements in the list based on an index.List collection elements can be duplicated or null elements can be saved.

List collections can manipulate collections based on indexes, so the List interface adds some interface methods to manipulate collections based on indexes to the Collection interface base.

Implementation Class for Collection Interface

The List collection has two common implementations, ArrayList and LinkedList, which are implemented internally with different data structures and have different usage choices for different scenarios.

ArrayList

In addition to inheriting and implementing the collection interface, the ArrayList class also implements the RandomAccess and Cloneable interfaces.Explains that ArrayList supports cloning and fast random access.

The internal data structure of the ArrayList is an array.

Default initialization capacity is 10

ArrayList collection from find, add, delete, modify element methods

Find Element Method: get, indexOf

// Finding elements directly from an index is efficient
public E get(int index) {
    rangeCheck(index);
    return elementData(index); // Returns elements directly from an array based on an index
}
    
    
// Depending on the size of the collection and the location of the element, there is no Return-1 for the element, and a looping traversal is used to find the element.
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}  

Add Element Method: add

// Adding elements, such as expansion, data copying, and so on, can be inefficient. If you want to add a large number of elements to a collection, you can specify a larger initial capacity through the construction method.
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Add modCount!!
    elementData[size++] = e;
    return true;
}


private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}


// Calculating capacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}


// Ensure capacity safety
private void ensureExplicitCapacity(int minCapacity) {
    modCount++; // Set structure modification counter (structure modification refers to those that change the size or location of the list, etc.)
    // Expansion is required when the minimum required capacity is larger than the array capacity
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}


// Expansion
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 times capacity
    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);
}

Delete element method: remove

// Deleting an element based on an index moves forward after the deletion position if the element is deleted from the beginning and middle, which is less efficient than deleting the end element.
public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        // Moving copies of elements
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // Assigning null explicitly allows garbage collection, --size modifies collection length after deleting elements

    return oldValue;
}
// Deletes based on a collection element, first loops to find the index of the location of the element to be deleted, then deletes based on the index.Compared with the index deletion method, there is one more step to looping up the index position of an element.
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;
}

// Delete Collection Elements
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
}

Modify element method:set

// Modify the element based on the index to point directly to the new element value.
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}
    

As you can see from the code above, the ArrayList collection retrieves elements more efficiently, more appropriately, and less efficiently for addition and deletion.

LinkedList

The LinkedList class also implements the Deque ue interface, which, unlike the List interface, does not support accessing elements through an index, so a LinkedList is a List collection and a double-ended queue.

private static class Node<E> {
    E item; // Current element
    Node<E> next; // Next Node
    Node<E> prev; // Last Node

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

LinkedList is a linked list data structure, which maintains an internal class Node as a node in the linked list, with first pointing to the first node and last pointing to the tail node.Each Node records references to the previous node, the next node, and the elements stored by the current node.

The chain table structure is shown in the following figure:

(Picture Source Network)

See what the LinkedList collection is suitable for (you can find it from the underlying data structure) by finding, adding, deleting, and modifying the element parts

Find Element Method:get

// Finding elements by index requires traversing through the top or bottom of the lookup because the chain table does not have an index.Different underlying data structures of ArrayList and LinkedList result in more efficient element lookup in the ArrayList collection because the underlying ArrayList is an array that can be retrieved directly from an index.
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

Node<E> node(int index) {
    // If the position of the element you are looking for is less than one-second the length of the set, traverse backward and forward; otherwise, traverse backward and forward, you know that the more you go to the middle, the less efficient it is
    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;
    }
}

Add Element Method: add

Because of the different underlying data structures, LinkedList is more efficient at increasing elements than ArrayList, which has operations such as expansion and copy.

// Add element to tail
public boolean add(E e) {
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null); // Create a new node
    last = newNode; // Point the new node to the last node
    if (l == null) 
        first = newNode;//  If newNode is the only element in the set (initially an empty set), then it also points to the first node (first)
    else
        l.next = newNode; // The reference to the next element of the original last node points to the new node (newNode)
    size++;
    modCount++;
}


// Adding elements at specified locations decreases the efficiency of inserting index closer to the middle and increases with the length of the collection
public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size) // index==size specifies that the collection is empty or that an element is added at the end of the collection
        linkLast(element);
    else
        linkBefore(element, node(index));
}

// Unlike lists and arrays, elements cannot be obtained directly from an index; lists need to be looped from beginning to end to get elements at specified locations.
Node<E> node(int index) {
    // If the position of the element you are looking for is less than one-second the length of the set, traverse backward and forward; otherwise, traverse backward and forward, so it is less efficient to move to the middle.
    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;
    }
}

// Insert element before succ node
void linkBefore(E e, Node<E> succ) {
    final Node<E> pred = succ.prev; 
    final Node<E> newNode = new Node<>(pred, e, succ);// Create a new node
    succ.prev = newNode; // 
    if (pred == null)
       // Explain that succ is the first node
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

Modify element method:set

LinkedList modifies elements by traversing to find them first. ArrayList can get elements directly from the index, so LinkedList is less efficient, and the closer the element is to the middle, the more obvious it is.

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

// Traverse element nodes based on index
Node<E> node(int index) {
    // If the position of the element you are looking for is less than one-second the length of the set, traverse backward and forward; otherwise, traverse backward and forward, so it is less efficient to move to the middle.

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

Delete element method: remove

There are no moving copies compared to ArrayList, so LinkedList deletes elements more efficiently than ArrayList

public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}

// Find target node based on index traversal
Node<E> node(int index) {
    // If the index is less than half the length of the collection
    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;
    }
}
    
E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}

LinkedList implements the Deque ue interface and is a two-end queue, so LinkedList contains the following common methods:

Interesting design in source code

Method redefinition

Multiple methods in the Collection interface in the source code are redefined in the List interface again. Since the List has inherited the Collection interface, why redefine it, historical reason?List before Collection?

Duplicate implementation of interfaces

AbstractList already implements the List interface, and ArrayList inherits AbstractList, whereas the ArrayList source implements the List interface.


The following answers were searched online:

It means he asked the developer about this, which is a mistake.Long ago considered valuable.

Don't know if this answer is correct?

https://stackoverflow.com/questions/2165204/why-does-linkedhashsete-extend-hashsete-and-implement-sete

Summary

Both List and Set collections inherit the Collection interface. Collection is the top-level interface of List and Set. There are two common implementation classes under the List interface, ArrayList and LinkedList. LinkedList implements the Deque interface, so LinkedList is both a List collection and a double-ended queue.

ArrayList is based on the array data structure, while LinkedList is based on the chain table data structure. From the data structure characteristics and source code implementation, ArrayList can get elements quickly according to the index. Adding elements requires the expansion and copy of the array, deleting elements requires the moving copy of the array, so the ArrayList collection is efficient for finding and modifying elements.Better, slightly less efficient for addition and deletion.

LinkedList's chain table data structure cannot get element nodes directly and quickly from the index, it must traverse from the top or the tail to the index position (if the index value is less than 1/2 of the length of the set, it will traverse from the head, otherwise it will traverse from the tail, so it will be less efficient to traverse when the index value is in the middle than at both ends.)When adding or deleting elements, only the upper and lower nodes need to be re-pointed to the new node object reference. There is no expansion, movement, etc. Therefore, LinkedList and Array List are more suitable for adding and deleting elements than Array List, and they are less efficient for finding operations.

For reprinting, please indicate the source: https://www.cnblogs.com/newobjectcc/p/10789188.html

Welcome to Public Number [NO Programming]

Topics: Java less network JDK