Artículos en profundidad de Java ~ 02. Implementación de la estructura de datos de cadena (JDK1.8)

Implementación de la estructura de datos de cadena

Artículo anterior

Introducción

       En los tipos de datos básicos de Java, la cadena de palabras clave no está incluida. La capa inferior de la cadena se realiza mediante la matriz de caracteres. El objeto String también es un objeto inmutable. Si observa la documentación de JDK, encontrará que cada una de las clases String parece modificar el método del objeto, que esencialmente crea un nuevo objeto String para contener el contenido de la cadena modificado. La cadena original no se ha movido en absoluto.

He escrito un blog sobre la estructura de datos de cadenas en el pasado, haga clic en el enlace para ingresar

Cuerda

Una matriz de caracteres que implementa referencias de cadenas.

private final char value[];

       Cuando nuevo un objeto de cadena. La cadena creada es en realidad una copia de la cadena de parámetros. Porque la cuerda es inmutable.

public String(String original) {
    
    
        this.value = original.value;
        this.hash = original.hash;
    }

método charAt

       La función de este método es principalmente devolver el carácter en la posición especificada de acuerdo con el índice Dado que la capa inferior de la cadena es una matriz de caracteres, se puede devolver directamente al juzgar si el índice es legal.

public char charAt(int index) {
    
    
        if ((index < 0) || (index >= value.length)) {
    
    
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

método de longitud

       El tamaño de la matriz de caracteres, que es la longitud de la cadena. Entonces, el método de longitud solo necesita devolver directamente el tamaño de la matriz

public int length() {
    
    
        return value.length;
    }

método isEmpty

       Para determinar si la cadena es una cadena vacía, solo necesita determinar si la longitud de la matriz es igual a 0.

public boolean isEmpty() {
    
    
        return value.length == 0;
    }

método igual

       El método equals no es lo mismo que ==. Cuando el contenido del elemento es el mismo, la dirección es diferente, entonces el resultado devuelto por == sigue siendo falso. Porque == se compara por dirección. E igual, incluso si la dirección es diferente, siempre que el contenido sea exactamente el mismo, devolverá verdadero.

public boolean equals(Object anObject) {
    
    
        if (this == anObject) {
    
    
            return true;
        }
        if (anObject instanceof String) {
    
    
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
    
    
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
    
    
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

método indexOf

       Devuelve la posición de la cadena de caracteres por carácter. El método de implementación se logra atravesando la matriz de caracteres, que se llama a través de la capa inferior, y el último método llamado es en realidad indexOfSupplementary

private int indexOfSupplementary(int ch, int fromIndex) {
    
    
        if (Character.isValidCodePoint(ch)) {
    
    
            final char[] value = this.value;
            final char hi = Character.highSurrogate(ch);
            final char lo = Character.lowSurrogate(ch);
            final int max = value.length - 1;
            for (int i = fromIndex; i < max; i++) {
    
    
                if (value[i] == hi && value[i + 1] == lo) {
    
    
                    return i;
                }
            }
        }
        return -1;
    }

método compareTo

       La función de este método es principalmente comparar el tamaño de dos cadenas. Para garantizar la eficiencia y evitar muchas operaciones innecesarias, la capa inferior utiliza la variable lim para almacenar los dos datos con la longitud de cadena más pequeña, de modo que se pueda reducir el número de bucles. El significado de if (c1! = C2) {return c1-c2} es que si dos cadenas tienen la misma longitud, use el código ASCll para comparar.

public int compareTo(String anotherString) {
    
    
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
    
    
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
    
    
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

método de reemplazo

       El método de reemplazo reemplaza los caracteres antiguos de la cadena con caracteres nuevos. Primero, juzgará si el personaje antiguo y el nuevo son iguales, si son iguales, no es necesario modificarlo. Si no son iguales, primero usará len para obtener la longitud de la matriz de caracteres, y luego usará val para almacenar la nueva matriz de caracteres. Utilice la variable i como índice para localizar la posición del carácter antiguo. Finalmente, guarde la nueva cadena con la nueva matriz de caracteres buf y regrese. Es decir, aunque este método consiste en modificar la función. Pero, en esencia, acaba de crear una nueva matriz de caracteres y la original permanece intacta.

public String replace(char oldChar, char newChar) {
    
    
        if (oldChar != newChar) {
    
    
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
    
    
                if (val[i] == oldChar) {
    
    
                    break;
                }
            }
            if (i < len) {
    
    
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
    
    
                    buf[j] = val[j];
                }
                while (i < len) {
    
    
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }

StringBuilder

Introducción

       StringBuilder es una secuencia de caracteres variable, la capa inferior hereda la clase abstracta de AbstractStringBuilder y su esencia aún se implementa mediante una matriz de caracteres. Pero la implementación de StringBuilder no es la misma que String, StringBuilder se parece más a una matriz dinámica de ArrayList. Porque, StringBuilder primero le dará a la matriz una longitud inicial y luego usará una variable para contar la longitud de la cadena. En otras palabras, en String, la longitud de la cadena es igual a la longitud de la matriz de caracteres. En StringBuilder, la longitud de la cadena puede no ser igual a la longitud de la matriz de caracteres. Además, una vez que la longitud de la cadena excede la longitud de la matriz de caracteres, su capa inferior llamará a un método para expandir la matriz de caracteres.

       Cuando lo hacemos por primera vez, su longitud inicial será el tamaño de la cadena +16

public StringBuilder(String str) {
    
    
        super(str.length() + 16);
        append(str);
    }

agregar método

       La función del método append es agregar caracteres o cadenas a la cadena original, el principio de implementación es agregar un elemento directamente detrás de la matriz de caracteres, como se ha dicho antes. La longitud de la cadena no es igual a la longitud de la matriz de caracteres. Así que directamente value [count ++] = c; está bien. Entonces, la pregunta es, ¿qué pasa si la longitud excede el rango de la matriz de caracteres? De hecho, llama al método secureCapacityInternal en la parte inferior.

private void ensureCapacityInternal(int minimumCapacity) {
    
    
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
    
    
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

       Este método expandirá la longitud de la matriz de caracteres. ¿Cuánto es la expansión? Como vimos en el método newCapacity, la expansión es 1 vez + 2. La razón para utilizar operaciones con bits es que la eficiencia de las operaciones con bits es mayor que la de la multiplicación.

private int newCapacity(int minCapacity) {
    
    
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
    
    
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

método de eliminación

       El método de eliminación elimina los elementos en el índice inicial y final. inicio es un intervalo abierto y final es un intervalo cerrado. Su principio de implementación es determinar primero si los dos índices son legales, y si son legales, llamarán al método System.arraycopy para lograr

public AbstractStringBuilder delete(int start, int end) {
    
    
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
    
    
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }

método deleteCharAt

       Este método es el mismo que el principio de eliminación en este momento. Primero, determine si el índice es legal y luego llame a System.arraycopy para lograr

public AbstractStringBuilder deleteCharAt(int index) {
    
    
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        System.arraycopy(value, index+1, value, index, count-index-1);
        count--;
        return this;
    }

método de inserción

       Este método se parece mucho a agregar. La diferencia es que append agrega elementos al final de la cadena de forma predeterminada, mientras que insert agrega elementos basados ​​en el índice. Si la longitud no es suficiente, se llamará al método para expandir

public AbstractStringBuilder insert(int offset, String str) {
    
    
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        if (str == null)
            str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        System.arraycopy(value, offset, value, offset + len, count - offset);
        str.getChars(value, offset);
        count += len;
        return this;
    }

método de reemplazo

       El reemplazo en StringBuilder es diferente de la implementación en String. En StringBuilder, el reemplazo se puede modificar de acuerdo con la cadena dentro del rango del índice inicial y el índice final. El método de implementación sigue siendo determinar primero el índice, luego reubicar el tamaño y luego llamar al método arraycopy para operar.

 public AbstractStringBuilder replace(int start, int end, String str) {
    
    
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (start > count)
            throw new StringIndexOutOfBoundsException("start > length()");
        if (start > end)
            throw new StringIndexOutOfBoundsException("start > end");

        if (end > count)
            end = count;
        int len = str.length();
        int newCount = count + len - (end - start);
        ensureCapacityInternal(newCount);

        System.arraycopy(value, end, value, start + len, count - end);
        str.getChars(value, start);
        count = newCount;
        return this;
    }

método inverso

       La función principal de este método es invertir la cadena. La llamada inferior es el método reverseAllValidSurrogatePairs

private void reverseAllValidSurrogatePairs() {
    
    
        for (int i = 0; i < count - 1; i++) {
    
    
            char c2 = value[i];
            if (Character.isLowSurrogate(c2)) {
    
    
                char c1 = value[i + 1];
                if (Character.isHighSurrogate(c1)) {
    
    
                    value[i++] = c1;
                    value[i] = c2;
                }
            }
        }
    }

StringBuffer

       Los métodos de StringBuffer y StringBuilder son básicamente los mismos, pero sus diferencias esenciales son bastante grandes. StringBuffer es una clase que existe en JDK1.0. Es segura para subprocesos y es ineficiente para verificar la sincronización de subprocesos. StringBuilder es una clase que solo proporciona JDK1.5. No es seguro para subprocesos y no realiza comprobaciones de sincronización de subprocesos, por lo que es más eficiente. Por supuesto, se recomienda utilizar StringBuilder en uso real

Salida formateada

       En JavaSE5, se introdujo printf () en lenguaje C; printf no usa "+" para conectarse, pero usa comas para separar

public static void main(String[] args) {
    
    
        System.out.printf("%d + %d = %d",5,9,5 + 9);
    }

System.out.format ()

       El método de formato se puede utilizar para los objetos PrintStream y PrintWriter, incluidos los objetos System.out. El método del formato y el método printf son equivalentes.

public static void main(String[] args) {
    
    
        System.out.format("%d + %d = %d",5,9,5 + 9);
    }

Clase de formateador

       En Java, todas las nuevas funciones de formato son manejadas por la clase java.util.Formatter. Piense en Formatter como un traductor, traducirá su cadena formateada y sus datos al resultado deseado. Cuando crea un objeto Formatter, necesita pasar cierta información a su constructor para indicarle dónde generar el resultado final.

Supongo que te gusta

Origin blog.csdn.net/qq_41424688/article/details/108562284
Recomendado
Clasificación