Java基础1-String/List/TreeSet/TreeMap

Java基础1-String/List/TreeSet/TreeMap

What is the difference between Stringbuffer and Stringbuilder?

  • Stringbuffer is thread-safe (the synchronized keyword is added), Stringbuilder is not safe, and the method is basically the same
  • There is a toStringCache field in the Stringbuffer. The explanation on the field is to return the cached value of the last toString. Once the StringBuffer is modified, the cached value will be cleared. Such as:
    • image-20210320163554956
    • ToStringCache will be used here. If you haven't changed it (!=null), you don't need to copy it, so it will speed up the running speed. However, due to the existence of the synchronized lock, the overall speed is still higher in Stringbuilder.
    • image-20210320165035946

What is the difference between ArrayList and LinkedList and vector?

  • This question should be viewed from the underlying data structure of the three:

    • The bottom layer of LinkedList is a linked list, so consider the advantages and disadvantages of the linked list-random is slower (the get method is available), but insertion and deletion are fast

      • From the constructor, we can know that this thing is a doubly linked list, and there are two pointers: first last
        • image-20210320180527519
      • Implementation of get:
        • First check to ensure the validity of the index, which is equivalent to an out-of-bounds check, and then call the node method to return the item attribute of the element-equivalent to value
          • image-20210320181731096
        • From the node method, we can know that the average time complexity of the get method is n/4, because it divides the traversal into two modes, forward/backward, so the longest does not exceed half of the length (size>>1 is Shift, equivalent to 2 but faster)
        • image-20210320180428816
    • The bottom layer of ArrayList is an array, so consider the advantages and disadvantages of the array-random search is fast, but insertion and deletion (especially the front part) is slow

      • From the source code, you can know that his get returns the subscript directly.
        • image-20210320181830050
      • But the add method is interesting. As you can see from the source code below, it calls the system arraycopy, so it is slow.
        • image-20210320182158721
      • Another point is that data confusion may occur in the iterator of ArrayList in a multi-threaded state.
    • The bottom layer of vector is also an array, which is very similar to ArrayList. Therefore, in most cases, we can use ArrayList as an enhanced version of vector:

      • image-20210320195959415
      • image-20210320201017503
      • However, we can observe from the source code below (get method) that vector is locked on the get method. . . . So it can be imagined that the efficiency in the multi-threading situation is low, but it is thread-safe, while ArrayList is not synchronized, and there is no other mechanism to ensure thread safety, so it can be considered unsafe.

        • //ArrayList
          public E get(int index) {
                      
                      
              rangeCheck(index);
          
              return elementData(index);
          }
          //vector
          public synchronized E get(int index) {
                      
                      
            if (index >= elementCount)
              throw new ArrayIndexOutOfBoundsException(index);
          
            return elementData(index);
           }
          
          
      • Furthermore, there is the difference in the expansion rules. ArrayList expands 1.5 times each time (approximately because of shifting), and vector adds a capacityIncrement each time (this value can be set during initialization)

        • //ArrayList
          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);
          }
          
          //vector
          private void grow(int minCapacity) {
                      
                      
            //而且俺发现,这个minCapacity在自动扩容的时候是一个一个扩容的。。。。这个效率低得离谱
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                             capacityIncrement : oldCapacity);
            if (newCapacity - minCapacity < 0)
              newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
              newCapacity = hugeCapacity(minCapacity);
            elementData = Arrays.copyOf(elementData, newCapacity);
          }
          
          
          //add算法扩容的时候参数是elementCount+1。。。。。就扩容了一个
          private void ensureCapacityHelper(int minCapacity) {
                      
                      
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
              grow(minCapacity);
          }
          
          
          public synchronized boolean add(E e) {
                      
                      
            modCount++;
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = e;
            return true;
          }
          

About TreeSet

  • It is implemented based on NavigableMap, and the TreeMap is passed in the structure. The code is extracted as follows:

  • private transient NavigableMap<E,Object> m;
    TreeSet(NavigableMap<E,Object> m) {
          
          
      this.m = m;
    }
    public TreeSet() {
          
          
      this(new TreeMap<E,Object>());
    }
    
  • We look through the source code of TreeMap and we can see that TreeMap implements NavigableMap

    • image-20210321000027719
  • So when we look at the source code of TreeSet, we can find that it is basically the method of calling NavigableMap (or TreeMap), but it doesn't use the value of TreeMap (filled with PRESENT directly), just look for it:

    • image-20210321000850846
  • Then look at the implementation of TreeMap

About TreeMap

 private transient Entry<K,V> root;
 // Red-black mechanics
 private static final boolean RED   = false;
 private static final boolean BLACK = true;
 //这是基于红黑树的实现的entry,可以预见一定还有一个改变color的方法
 static final class Entry<K,V> implements Map.Entry<K,V> {
    
    
   K key;
   V value;
   Entry<K,V> left;
   Entry<K,V> right;
   Entry<K,V> parent;
   boolean color = BLACK;
   /**
          * Make a new cell with given key, value, and parent, and with
          * {@code null} child links, and BLACK color.
          */
   Entry(K key, V value, Entry<K,V> parent) {
    
    
     this.key = key;
     this.value = value;
     this.parent = parent;
   } 
   //其他还有些getset以及equal和tostring就省略了
 }
  • get method

  • public V get(Object key) {
          
          
      //只是调用getEntry,然后做了一个判空
      Entry<K,V> p = getEntry(key);
      return (p==null ? null : p.value);
    }
    	//getEntry就是走红黑树
    final Entry<K,V> getEntry(Object key) {
          
          
      // Offload comparator-based version for sake of performance
      if (comparator != null)
        return getEntryUsingComparator(key);
      if (key == null)
        throw new NullPointerException();
      @SuppressWarnings("unchecked")
      Comparable<? super K> k = (Comparable<? super K>) key;
      Entry<K,V> p = root;
      while (p != null) {
          
          
        int cmp = k.compareTo(p.key);
        if (cmp < 0)
          p = p.left;
        else if (cmp > 0)
          p = p.right;
        else
          return p;
      }
      return null;
    }
    
  • Put method

  • public V put(K key, V value) {
          
          
      Entry<K,V> t = root;
    	//若树为空,则点作为root节点
      if (t == null) {
          
          
        compare(key, key); // type (and possibly null) check
    
        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
      }
      int cmp;
      Entry<K,V> parent;
      // split comparator and comparable paths
     	// 比较器
      Comparator<? super K> cpr = comparator;
      // 走树,找到空节点的parent
      if (cpr != null) {
          
          
        do {
          
          
          parent = t;
          cmp = cpr.compare(key, t.key);
          if (cmp < 0)
            t = t.left;
          else if (cmp > 0)
            t = t.right;
          else
            return t.setValue(value);
        } while (t != null);
      }
      else {
          
          
        if (key == null)
          throw new NullPointerException();
        @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
        do {
          
          
          parent = t;
          cmp = k.compareTo(t.key);
          if (cmp < 0)
            t = t.left;
          else if (cmp > 0)
            t = t.right;
          else
            return t.setValue(value);
        } while (t != null);
      }
      //插入节点
      Entry<K,V> e = new Entry<>(key, value, parent);
      if (cmp < 0)
        parent.left = e;
      else
        parent.right = e; 
      fixAfterInsertion(e);
      size++;
      modCount++;
      return null;
    }
    //调整插入后的红黑树
    private void fixAfterInsertion(Entry<K,V> x) {
          
          
      x.color = RED;
    
      while (x != null && x != root && x.parent.color == RED) {
          
          
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
          
          
          Entry<K,V> y = rightOf(parentOf(parentOf(x)));
          if (colorOf(y) == RED) {
          
          
            setColor(parentOf(x), BLACK);
            setColor(y, BLACK);
            setColor(parentOf(parentOf(x)), RED);
            x = parentOf(parentOf(x));
          } else {
          
          
            if (x == rightOf(parentOf(x))) {
          
          
              x = parentOf(x);
              rotateLeft(x);
            }
            setColor(parentOf(x), BLACK);
            setColor(parentOf(parentOf(x)), RED);
            rotateRight(parentOf(parentOf(x)));
          }
        } else {
          
          
          Entry<K,V> y = leftOf(parentOf(parentOf(x)));
          if (colorOf(y) == RED) {
          
          
            setColor(parentOf(x), BLACK);
            setColor(y, BLACK);
            setColor(parentOf(parentOf(x)), RED);
            x = parentOf(parentOf(x));
          } else {
          
          
            if (x == leftOf(parentOf(x))) {
          
          
              x = parentOf(x);
              rotateRight(x);
            }
            setColor(parentOf(x), BLACK);
            setColor(parentOf(parentOf(x)), RED);
            rotateLeft(parentOf(parentOf(x)));
          }
        }
      }
      root.color = BLACK;
    }
    

There are red-black trees

image-20210321002726730
  • Claim:
    • The root node is black
    • Leaf nodes are black empty nodes that do not store data
    • Any two adjacent nodes cannot be red at the same time
    • There are the same number of black nodes from any node to its reachable leaf node
  • Non-strictly balanced binary tree, so sometimes complex rotation balancing operations are not required for each insertion, at the cost of a slight decrease in search efficiency (the order of magnitude is unchanged)

Guess you like

Origin blog.csdn.net/symuamua/article/details/115038343