Chapter 2 Collection-Java source notes

1 ArrayList source code analysis and design ideas

1.1 Overall structure

ArrayList is an array structure.

  • Allow put null value, it will automatically expand;
  • The time complexity of methods such as size, isEmpty, get, set, and add are all O (1);
  • It is not thread-safe. In the case of multi-threading, it is recommended to use the thread-safe class: Collections # synchronizedList;
  • Enhance the for loop, or use an iterator to iterate, if the array size is changed, it will quickly fail and throw an exception.

1.2 Initialization

Three methods: direct initialization without parameters, initialization of specified size, initialization of specified initial data.

// 无参数直接初始化
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 // 指定大小初始化
 public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
 public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
  • Direct initialization without parameters, the default size is an empty array, not 10. 10 is the value of the first expansion.

1.3 New and expanded implementation

  • 1.5 times the original capacity after expansion;
  • The maximum expansion is Integer.MAX_VALUE;
  • When adding, there is no strict check on the value, so ArrayList allows null values.
  • To expand the essence , create a new array, and then copy the old array. It consumes more performance, so specify the data size as much as possible to avoid frequent expansion.

1.4 Delete

Delete an index indexed element, the elements behind will move forward one bit.

1.5 Iterator

int cursor;// 迭代过程中,下一个元素的位置,默认从 0 开始。
int lastRet = -1; // 新增场景:表示上一次迭代过程中,索引的位置;删除场景:为 -1。
int expectedModCount = modCount;// expectedModCount 表示迭代过程中,期望的版本号;modCount 表示数组实际的版本号。
public boolean hasNext() {
            return cursor != size;
        }
 public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

2 LinkedList source code analysis

2.1 Overall structure

Insert picture description here
Node code

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

2.2 Add, delete

Can be added from the beginning to the end, some can be deleted from the beginning

2.3 Node query

If the index is in the first half, start the search from the beginning, otherwise start the search from the back, which improves efficiency. size >> 1 means shift right by 1 bit, which is equivalent to 2.

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

2.4 Iterator

Implement the ListIterator interface and support bidirectional iteration, that is, you can iterate from the beginning or the end.

3 List of interview questions

3.1 Talk about understanding of ArrayList and LinkedList

You can answer the overall architecture first, and then break through from a certain detail. For example, the bottom of ArrayList is an array, and its APIs are all encapsulation of the bottom array.

3.2 Expansion problems

1. ArrayList is constructed without parameter constructor. Now add a value into it. At this time, what is the size of the array and what is the maximum available size before the next expansion?
At this time, the size of the array is 1, and the maximum available size before the next expansion is 10. The default value for the first expansion is 10.
2. If I add new values ​​to the list continuously, when I increase to the 11th, the size of the array is How many?
oldCapacity + (oldCapacity >> 1), the next expansion is 1.5 times. That is from 10 to 15.
3. After the array is initialized and a value is added, if I use the addAll method and add 15 values ​​at once, what is the size of the final array?
After initialization, add a value. At this time, the default capacity expansion is 10, and then add 15 values ​​at once. The capacity expansion is 1.5 times, that is, 15, it still cannot be satisfied. There is a strategy at this time:

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

Direct expansion to 16.
4. Now I have a large array to copy, the original array size is 5k, how can I copy it quickly?
5k is a relatively large value, and the specified capacity is 5k when the array is initialized. If the initialization does not specify a size, it will be frequently expanded, with a large number of copies, affecting performance.
5. Why does capacity expansion consume performance?
The bottom layer uses System.arraycopy to copy the original array to the new array, so it consumes performance.
6. Is there anything worth learning from the source code expansion process?

  • Automatic expansion, users do not need to care about changes in the underlying data structure. Capacity expansion is based on a 1.5-fold growth. The growth in the early stage is relatively slow, and the growth in the later stage is relatively large. The size of data used in most jobs is not large. There is utilization to save performance. The later growth is large, which is conducive to rapid expansion.
  • During the expansion process, pay attention to the array overflow, which is not less than 0 and not greater than the maximum value of Integer.

3.3 Deletion problems

1. There is an ArrayList, the data is 2, 3, 3, 3, 4 and there are three 3s in the middle. Now I use for (int i = 0; i <list.size (); i ++), I want to change the value to 3 Elements deleted, can I delete them cleanly? What is the result of the final deletion and why? The deletion code is as follows:

List<String> list = new ArrayList<String>() {{
            add("2");
            add("3");
            add("3");
            add("3");
            add("4");
        }};
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("3")) {
                list.remove(i);
            }
        }

Can't. Insert picture description here
2. Still the above ArrayList array, can we delete by enhancing the for loop?
No, it will report an error. Because the enhanced for loop process actually calls the iterator's next () method. When you call the list # remove () method to delete, the value of modCount will be +1, but the value of expectedModCount in the iterator has not changed. , Causing the next time the iterator executes the next () method, expectedModCount! = ModCount will report ConcurrentModificationException error.
See: Why does Alibaba prohibit the remove / add operation of elements in the foreach loop
3. The above array, if it can be deleted using the Iterator.remove () method when deleting, why?
Yes, because the Iterator.remove () method will assign the latest modCount to the expectedModCount during execution, so that during the next cycle, both modCount and expectedModCount will be equal.
4. Are the above three questions the same for LinkedList?
Yes, although the underlying structure of LinkedList is a doubly linked list, but for the above three problems, the results are consistent with ArrayList.

3.4 Contrast problems

1. What is the difference between ArrayList and LinkedList?
The underlying logic of the two is different. ArrayList is an array, and LinkedList is a doubly linked list. The APIs implemented around the bottom are also different, bla, bla, etc.
2. What are the different application scenarios of ArrayList and LinkedList?
ArrayList is suitable for the scenario of fast searching without frequent addition and deletion, while LinkedList is suitable for the scenario of frequent addition and deletion and few queries.
3. Does ArrayList and LinkedList have the maximum capacity? The
former has the largest integer, the latter is theoretically wireless, but the actual size is int size, so there are only integers.
4. How does ArrayList and LinkedList handle null values?
Both run the addition and deletion of null values, but ArrayList deletes null values ​​from the beginning.
5. Are ArrayList and LinedList thread-safe, why?
No. In a multi-threaded environment, operations such as addition and deletion are not synchronized.
6. How to solve the problem of thread safety?
There are thread-safe lists in Collections, Collections.synchronizedList (), or CopyOnWriteArrayList.

3.5 Other types of topics

1. Describe the doubly linked list?
slightly.
2. Describe the addition and deletion of doubly linked lists
.

4 HashMap source code analysis

Published 97 original articles · praised 3 · 10,000+ views

Guess you like

Origin blog.csdn.net/qq_39530821/article/details/105347974