List collection is so simple [source code analysis]

foreword

Statement, this article uses jdk1.8

The previous article has already talked about the overview of Collection: Collection overview , which introduces some basics.

Now this article mainly talks about the three subclasses of the List collection:

  • ArrayList
    • The underlying data structure is an array. thread unsafe
  • LinkedList
    • The underlying data structure is a linked list. thread unsafe
  • Vector
    • The underlying data structure is an array. thread safety

This article mainly takes a look at how their more important methods are implemented, what needs to be paid attention to, and finally compare when to use which~

Before reading this article, it is best to have a little bit of data structure foundation: Java implements singly linked list , stack and queue are so simple , binary tree is so simple

 

 

 

 

Of course, if there is something wrong, please bear with me and correct me in the comments~

1. ArrayList analysis

 

 

First of all, let's talk about the ArrayList collection, which is a collection we use a lot~

First, let's take a look at the properties of ArrayList:

 

 

According to the above, we can clearly find: The bottom layer of ArrayList is actually an array , and there is a concept of expansion in ArrayList. Because of its expansion, it can achieve "dynamic" growth.

1.2 Construction method

Let's take a look at the construction method to confirm that we are right above:

 

 

1.3Add method

The add method can be said to be the most important method of ArrayList. Let's take a look at it:

 

 

1.3.1add (E e)

step:

  • Check if expansion is required
  • insert element

First, let's look at this method:


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

The method is very short, and we can guess what it does based on the method name:

  • Confirm the list capacity, try adding 1 to the capacity, and see if it is necessary
  • add element

Next, let's see if this small capacity (+1) meets our needs:

 

 

Then call ensureExplicitCapacity()to determine the explicit capacity, let's also see how this method is implemented:

 

 

So, let's see grow()how it works~

 

 

Go in and see copyOf()how:

 

 

So far, we can know add(E e)the basic implementation:

  • First, check whether the capacity of the array is sufficient
    • Enough: add directly
    • Not Enough: Expansion
      • Expanded to 1.5 times the original
      • After the first expansion, if the capacity is still smaller than minCapacity, the capacity is expanded to minCapacity.

1.3.2add(int index, E element)

step:

  • Check the corner mark
  • Space check, expand if necessary
  • insert element

Let's take a look at the implementation of insert:

 

We found that the bottom layer of the add method of ArrayList related to expansion is actually arraycopy()implemented

See arraycopy(), we can find: this method is written by C/C++ , not implemented by Java:

 

 

In general: arraycopy()It is still a relatively reliable and efficient method.

Refer to R's answer: www.zhihu.com/question/53…

1.4 get method

  • Check the corner mark
  • return element

 

 




   // 检查角标
   private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
	
	// 返回元素
    E elementData(int index) {
        return (E) elementData[index];
    }

1.5 set method

step:

  • Check the corner mark
  • alternative element
  • return old value

 

 

1.6 remove method

step:

  • Check the corner mark
  • remove element
  • Calculate the number of moves that need to be moved and move
  • Set to null to let Gc recycle

 

 

1.7 Re-description of details

  • ArrayList is implemented based on dynamic arrays . When adding or deleting, the copying of the array is required .
  • The default initialized capacity of ArrayList is 10, and each time it is expanded, it increases by half of the original capacity, that is, it becomes 1.5 times the original capacity.
  • When deleting elements, the capacity will not be reduced. If you want to reduce the capacity, call trimToSize()
  • It is not thread safe. It can hold null values.

 

 

References:

Second, the difference between Vector and ArrayList

Vector is a class of jdk1.2, a relatively old collection class.

 

 

The bottom layer of Vector is also an array, and the biggest difference from ArrayList is: synchronization (thread safety)

Vector is synchronous, we can see it from the method~

 

 

In the case of non-synchronization, we generally use ArrayList instead of Vector~

If you want ArrayList to achieve synchronization, you can use the Collections method: List list = Collections.synchronizedList(new ArrayList(...));, you can achieve synchronization~

There is another difference:

  • ArrayList expands 0.5 times on the original basis when the underlying array is not enough, and Vector expands 1 times. ,

 

 

 

 

For the analysis of Vector source code, please refer to:

Three, LinkedList analysis

 

 

The bottom layer of LinkedList is a doubly linked list ~ If you are unfamiliar with linked lists, you can look at my singly linked list first (I haven't done the practice of doubly linked list) [ Java implements singly linked list ]

After understanding the singly linked list, the doubly linked list is not difficult.

 

 

Structurally, we also see that LinkedList implements the Deque interface , so we can operate LinkedList like queues and stacks ~

 

 

There are only a few LinkedList variables, because we also found it when we operated the singly linked list: with the head node, we can get other data. (The same is true for doubly linked lists)

 

 

3.1 Construction method

There are two construction methods for LinkedList:

 

 

3.2add method

If you have done linked list exercises, you are not unfamiliar with the following code~

  • The add method actually adds an element to the end of the linked list

    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);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

3.3remove method

 

 

 

 

In fact, it is the operation of the following figure:

 

 

3.4 get method

You can see that the get method is implemented in two pieces of code:

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

Let's go in and see what the specific implementation looks like:

 

 

3.5set method

The set method is actually similar to the get method. It is judged whether to traverse from the beginning or the end according to the subscript.


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

......The methods of LinkedList are much more than those of ArrayList, so I won't explain them one by one here. For details, please refer to:

4. Summary

In fact, the source code of the collection does not seem to be very difficult. If you encounter problems, you can turn it over and you should be able to understand it~

ArrayList, LinkedList, and Vector are the common knowledge points in interview questions. Here's a brief summary:

ArrayList:

  • The underlying implementation is an array
  • The default initialized capacity of ArrayList is 10, and each time it is expanded, it increases by half of the original capacity, that is, it becomes 1.5 times the original capacity.
  • When adding or deleting, the copying of the array is required (the navite method is implemented by C/C++)

LinkedList:

  • The underlying implementation is a doubly linked list [a doubly linked list is convenient for forward traversal]

Vector:

  • The bottom layer is an array, which is now less used and replaced by ArrayList for two reasons:
    • All methods of Vector are synchronous, and there is a performance penalty .
    • The initial length of Vector is 10. When it exceeds the length, it grows at a rate of 100%, which consumes more memory than ArrayList .
    • Reference: www.zhihu.com/question/31…

In general: ArrayList is used for queries, and LinkedList is used for additions and deletions.

The slowness of ArrayList additions and deletions is not absolute ( in the case of large numbers, it has been tested ):

  • If adding elements is always using add()(adding to the end), then ArrayList is faster
  • It is also faster to delete the elements at the end of the ArrayList [ without copying and moving the position]
  • As for if the middle position is deleted, ArrayList is faster !

But in general: LinkedList is still used for more additions and deletions, because the above situation is extreme~

References:

 

 

Article directory navigation : zhongfucheng.bitcron.com/post/shou-j…

If there are any mistakes in the article, please correct me, and we can communicate with each other. Students who are used to reading technical articles on WeChat and want to get more Java resources can follow WeChat public account: Java3y . For everyone's convenience, I just created a new QQ group: 742919422 , you can also go to communicate.


 

 

Guess you like

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