Interpretation of java collection source code

Java collections are divided into two categories: Collection and Map

 First uncheck the class debugging that you don’t want to enter, so that you can easily see the source code of the collection directly in the debugging

ArrayList

ArrayList implements the List interface and uses an array structure to store data

1) ArrayList maintains an array of Object type elementData.
transient Object[] elementData; //transient means instant, short, which means that the attribute will not be serialized
2
) When creating an ArrayList object, if you use If there is no parameter constructor, the initial elementData capacity is 0.
When adding for the first time, expand elementData to 10. If you need to expand again, expand elementData to 1.5 times.
3) If a constructor with a specified size is used, the initial elementData capacity is the specified size. If expansion is required,
the elementData can be expanded directly by 1.5 times.

Next, use the following code to debug and see

   public ArrayList() {
        //在构造空大小的ArrayList集合中会默认存下面的数
        // 使用这个数组是在添加第一个元素的时候会扩容到默认大小10

        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

 You will come here when you execute the add method

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  private void ensureExplicitCapacity(int minCapacity) {
        //modCount记录修改的数据
        modCount++;
        //这里minCapacity是10,意味着扩容10
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    public boolean add(E e) {
       //确实是否扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
 private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

grow expands the size

  private void grow(int minCapacity) {
        // overflow-conscious code
        //获取元素长度,如果是第一次则是0
        int oldCapacity = elementData.length;
        //按照1.5倍扩容
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
           //如果是第一次获取则给它默认赋值10
           //如果新容量
            newCapacity = minCapacity;
       //如果新容量已经超过最大容量了,则使用最大容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //扩容使用copyof 以新容量拷贝出来一个新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

After the first add, 10 default spaces are given

Given a constructor:

public static void main(String[] args) {
        List list=new ArrayList(8);
        for (int i=0;i<15;i++){
            list.add(i);
        }
    }

If the constructor does not assign a value, the size is the default size

When the above i is executed to 8, expand the capacity by 1.5 times

 Vector underlying structure and source code analysis

The source code of Vector and ArrayList is almost the same, and the added thread safety is synchronized and expansion multiple

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

The difference between ArrayList and Vector. 

ArrayList mutable array unsafe, efficient If there is no parameter, the default size is 10, and then the capacity will be expanded by 1.5 times each time
Vector mutable array Object[] safe, not efficient If there is no parameter, the default size is 10, and then the capacity will be expanded by 2 times each time

LinkedList

1) The bottom layer of LinkedList implements the characteristics of doubly linked list and double-ended queue

2) Any element (including null) can be added.

3) Threads are not safe and synchronization is not implemented.

1. The bottom layer of LinkedList maintains a doubly linked list.

2. Two attributes first and last are maintained in LinkedList to point to the first node and the last node respectively.

3. Each node (Node object) maintains three attributes: prev, next, and item. Among them, prev points to the previous node, and next points to the next node. Finally, a doubly linked list is implemented.

4. Therefore, the addition and deletion of elements of LinkedList is not done through arrays, which is relatively efficient.

5. Simulate a simple doubly linked list

Structure as shown

LinkedList simple addition, deletion and modification + bug source code underlying test

 public static void main(String[] args) {
       // List list=new LinkedList();
        LinkedList list=new LinkedList();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println("linkedList=" + list);
        //删除一个节点
        //list.remove();
        list.remove();
        list.remove(2);
        System.out.println("linkedList=" +list);
        //修改某个节点对象
        list.set(1,50);
        System.out.println("linkedList=" + list);
        //得到某个节点对象
        System.out.println(list.get(2));

        //因为LinkedList是实现了List接口,遍历方式
        for (Object i:list){
            System.out.println(i);
        }
    }

When the break point enters the internal code, you can see that there are three attributes: size, first (pointing to the first element) and last (pointing to the last element). 

 LinkedList has two methods for adding elements: add(E e)  and  add(int index,E e). Add elements at the end and add elements at the specified position, respectively. Here we use to add elements at the end.

   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 自增  
    size++;
    modCount++;
}

For the first addition, first and last and newNode will all point to the new node.

After adding it a second time, the

 The main methods of deleting nodes are

remove(int index)、remove(Object object)和remove()

Among them, remove() is to delete the head node and execute removeFirst

  public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
  private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //把原来头部节点的值置为空
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        //头节点指向原来头节点的下一个节点
        first = next;
        if (next == null)
            last = null;
        else
            //有第二个节点的话,就把头节点的前置节点指向null
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

If delete the executed Object.

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

 

 It can be seen from the figure that the link in the middle is broken, and the link before and after the deleted element is linked.

underlying structure Efficiency of adding and deleting Check efficiency
ArrayList mutable array

lower

Array expansion

higher
LinkedList Doubly linked list Higher, appended through the linked list lower

Set interface

1) Unordered has no index.

2) Duplicate elements are not allowed.

3) Commonly used implementation classes are HashSet, TreeSet

HashSet

1) HashSet implements the Set interface
2) HashSet is actually a HashMap
3) It can store null values, but there can only be one null
4) HashSet does not guarantee that the elements are in order, it depends on the result of the index after hashing. (That is, there is no
guarantee that the order in which elements are stored is consistent with the order in which they are retrieved)
| 5) There cannot be repeated elements/objects

The bottom layer of HashSet is: array + linked list + red-black tree.

1. The bottom layer of HashSet is HashMap
2. When adding an element, first get the hash value - it will be converted into -> index value
3. If the number of elements in a linked list reaches TREEIFY_THRESHOLD (default is 8), and the size of the table >= MIN_TREEIFY_CAPACITY( The default is 64), and it will be treed (red-black tree).
4. Find the storage data table and see if there is an element stored in this index position.
5. If not, add
6. If there is, call equals for comparison. If they are the same Just give up adding, if it is not the same, add to the end

Use simple data for bugs to see the source code

    public static void main(String[] args) {
        HashSet hashSet=new HashSet();
        hashSet.add("java");    //到此位置,第1次添加
        hashSet.add("php");     //到此为止,第2次添加
        hashSet.add("java");     //添加失败
        System.out.println(hashSet);
    }
 public boolean add(E e) {
        //e=java
        return map.put(e, PRESENT)==null;
    }

 

  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        //定义了辅助变量
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //在表的长度为0时,第一次扩容,到 16 个空间.
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //根据key,得到hash去计算该key应该存放到table表的哪个索引位置.
        //并把这个位置的对象,赋值给p
        //判断p是否为null
        //如果p为空则说明没有这个对象,就创建一个 Node (key="java",value=PRESENT)
        //就放在该位置tab[i]= newNode(Node,key,value,null)
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            //在需要局部辅助变量的时候创建.
            Node<K,V> e; K k;
            //如果当前添加的hash索引一样
            //并且满足以下条件任何之一
            //1、准备加入的 key 和 p 指向的 Node 结点的 key 是同一个对象
            //2、key不等于null并且key的值等于准备加入的p指向的Node节点的key相同
             //就不能加入
            if (p.hash == hash && 
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //判断p是不是一颗红黑树如果是的话,通过树状结构添加
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //遍历链表
                for (int binCount = 0; ; ++binCount) {
                      //依次遍历添加的链表数值
                    //立即判断 该链表是否已经达到 8 个结点
                    // , 就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
                    //这里e赋值p表的下一个,同时实现了迭代效果
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //在转成红黑树时,在treeifyBin内部进行扩容
                        //判断条件
                    // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                // resize();
                    // 如果上面条件成立,先 table 扩容. 
                // 只有上面条件不成立时,才进行转成红黑树
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //如果要添加的e指向的节点的key和原链表中有相等的,则不添加,直接break跳出循环
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                
                afterNodeAccess(e);
                return oldValue;
            }
        }
        //size 就是我们每加入一个节点Node(k,n,h,next),size++
        ++modCount;
        if (++size > threshold)
            resize();  //扩容
         //添加操作
        afterNodeInsertion(evict);
        return null;
    }

The critical value (threshold) is 16*load factor (loadFactor) is 0.75 = 12.
If the table array uses a critical value of 12, it will be expanded to 16 * 2 = 32, and 
the new critical value is 32*0.75 = 24, and so on. 

Test adding expansion, the default proximity value is 12.

        HashSet hashSet=new HashSet();
        for (int i=0;i<15;i++){
            //在同一链上添加了15个元素
            hashSet.add(new A(i));
        }

When there are 8 elements in the linked list, expand the capacity, and when the length of the table reaches 64, perform red-black tree treeing.

 

 Next, let's look at the expanded code

  final Node<K,V>[] resize() {
         //记录表
        Node<K,V>[] oldTab = table;
         //存储表长
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //记录上表的临近值,上表的临界值为threshold;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //新的容量按照两倍来扩容
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
               
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
           //新的临近值计算=0.75*表长
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
   
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        //将旧表的数据拷贝到新表中
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

Guess you like

Origin blog.csdn.net/qq_20314141/article/details/130304015