Java 基础 1 cadena / Lista / TreeSet / TreeMap

Java 基础 1 cadena / Lista / TreeSet / TreeMap

¿Cuál es la diferencia entre Stringbuffer y Stringbuilder?

  • Stringbuffer es seguro para subprocesos (se agrega la palabra clave sincronizada), Stringbuilder no es seguro y el método es básicamente el mismo
  • Hay un campo toStringCache en el Stringbuffer. La explicación en el campo es devolver el valor en caché del último toString. Una vez que se modifica el StringBuffer, el valor en caché se borrará. Tal como:
    • imagen-20210320163554956
    • ToStringCache se usará aquí. Si no lo ha cambiado (! = Null), no necesita copiarlo, por lo que acelerará la velocidad de ejecución. Sin embargo, debido a la existencia del bloqueo sincronizado, el la velocidad es aún mayor en Stringbuilder.
    • imagen-20210320165035946

¿Cuál es la diferencia entre ArrayList y LinkedList y vector?

  • Esta pregunta debe verse desde la estructura de datos subyacente de los tres:

    • La capa inferior de LinkedList es una lista enlazada, así que considere las ventajas y desventajas de la lista enlazada: el método aleatorio es más lento (el método de obtención está disponible), pero la inserción y eliminación son rápidas

      • Desde el constructor, podemos saber que esta cosa es una lista doblemente enlazada, y hay dos punteros: primero último
        • imagen-20210320180527519
      • Implementación de get:
        • Primero verifique para garantizar la validez del índice, que es equivalente a una verificación fuera de límites, y luego llame al método de nodo para devolver el atributo de elemento del elemento equivalente a valor
          • imagen-20210320181731096
        • A partir del método de nodo, podemos saber que la complejidad de tiempo promedio del método de obtención es n / 4, porque divide el recorrido en dos modos, adelante / atrás, por lo que el más largo no excede la mitad de la longitud (tamaño >> 1 es Shift, equivalente a 2 pero más rápido)
        • imagen-20210320180428816
    • La capa inferior de ArrayList es una matriz, así que considere las ventajas y desventajas de la búsqueda aleatoria de matrices que es rápida, pero la inserción y eliminación (especialmente la parte frontal) es lenta

      • Por el código fuente, puede saber que su get devuelve el subíndice directamente.
        • imagen-20210320181830050
      • Pero el método add es interesante.Como puede ver en el código fuente a continuación, llama al sistema arraycopy, por lo que es lento.
        • imagen-20210320182158721
      • Otro punto es que puede ocurrir confusión de datos en el iterador de ArrayList en un estado de subprocesos múltiples.
    • La capa inferior del vector también es una matriz, que es muy similar a ArrayList. Por lo tanto, en la mayoría de los casos, podemos usar ArrayList como una versión mejorada de vector:

      • imagen-20210320195959415
      • imagen-20210320201017503
      • Sin embargo, podemos observar en el código fuente a continuación (método get) que el vector está bloqueado en el método get. . . . Por lo tanto, se puede imaginar que la eficiencia en la situación de subprocesos múltiples es baja, pero es segura para subprocesos, mientras que ArrayList no está sincronizado y no hay otro mecanismo para garantizar la seguridad de subprocesos, por lo que puede considerarse inseguro.

        • //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);
           }
          
          
      • Además, existe la diferencia en las reglas de expansión. ArrayList se expande 1.5 veces cada vez (aproximadamente debido al cambio), y el vector agrega un aumento de capacidad cada vez (este valor se puede establecer durante la inicialización)

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

Sobre TreeSet

  • Se implementa en base a NavigableMap, y se pasa el TreeMap en la estructura. El código se extrae de la siguiente manera:

  • private transient NavigableMap<E,Object> m;
    TreeSet(NavigableMap<E,Object> m) {
          
          
      this.m = m;
    }
    public TreeSet() {
          
          
      this(new TreeMap<E,Object>());
    }
    
  • Miramos el código fuente de TreeMap y podemos ver que TreeMap implementa NavigableMap

    • imagen-20210321000027719
  • Entonces, cuando miramos el código fuente de TreeSet, podemos encontrar que es básicamente el método para llamar a NavigableMap (o TreeMap), pero no usa el valor de TreeMap (lleno directamente con PRESENT), solo búsquelo:

    • imagen-20210321000850846
  • Luego mire la implementación de TreeMap

Acerca de 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就省略了
 }
  • obtener el método

  • 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;
    }
    
  • Ponga el método

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

Hay arboles rojo-negros

imagen-20210321002726730
  • Afirmar:
    • El nodo raíz es negro
    • Los nodos hoja son nodos negros vacíos que no almacenan datos
    • Dos nodos adyacentes no pueden ser rojos al mismo tiempo
    • Hay el mismo número de nodos negros desde cualquier nodo hasta su nodo hoja accesible
  • Árbol binario no estrictamente balanceado, por lo que a veces no se requieren operaciones complejas de balanceo de rotación para cada inserción, a costa de una ligera disminución en la eficiencia de búsqueda (el orden de magnitud no cambia)

Supongo que te gusta

Origin blog.csdn.net/symuamua/article/details/115038343
Recomendado
Clasificación