Java 基础 1 cadena / Lista / TreeSet / TreeMap
Directorio de artículos
¿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:
- 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.
¿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
- 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
- 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)
- 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
- Desde el constructor, podemos saber que esta cosa es una lista doblemente enlazada, y hay dos punteros: primero último
-
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.
- 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.
- Otro punto es que puede ocurrir confusión de datos en el iterador de ArrayList en un estado de subprocesos múltiples.
- Por el código fuente, puede saber que su get devuelve el subíndice directamente.
-
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:
-
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
-
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:
-
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
- 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)