Análisis de código fuente JavaSE-String, StringBuilder y StringBuffer

Directorio

Introduccion

Cuerda

Herencia

Variables miembro y métodos de construcción.

StringBuilder 和 StringBuffer

Herencia

AbstractStringBuilder

Variables miembro y métodos de construcción.

Método de expansión

método append ()

StringBuilder

Método de construcción

método append ()

Método toString ()

StringBuffer

Variables miembro y métodos de construcción.

método append ()

Método toString ()

Resumen


Introduccion


Bajo JDK1.8, String, StringBuilder y StringBuffer se usan comúnmente para manipular cadenas. String es una secuencia inmutable de caracteres, y StringBuilder y StringBuffer son secuencias variables de caracteres. StringBuilder no es seguro para subprocesos, String y StringBuilder son seguros para subprocesos. Analicemos su implementación interna.

Cuerda


Herencia


public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence

Como puede ver, String implementa las interfaces Serializable , Comparable y CharSequence . Serializable es una marca serializable. La interfaz comparable contiene el método compareTo () . La interfaz CharSequence contiene los métodos charAt () , length () , subSequence () y toString () .

Variables miembro y métodos de construcción.


private final char value[];

private int hash; // Default to 0

public String() {
    this.value = "".value;
}

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

public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}

public String(char value[], int offset, int count) {
    if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count <= 0) {
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        if (offset <= value.length) {
            this.value = "".value;
            return;
        }
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

La cadena utiliza una matriz char [] constante para almacenar la cadena, que es inmutable. La matriz Char [] se convierte al tipo String , que es una copia profunda a través del método Arrays copyOff (), copiando la matriz char [] local y asignándola a la matriz char [] constante .

StringBuilder 和 StringBuffer


Herencia


public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

 

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

Se puede ver que las clases e interfaces implementadas por StringBuilder y StringBuffer son exactamente las mismas, y las interfaces Serializable y CharSquence se implementan como String . Lo más importante es que la clase abstracta heredada AbstractStringBuilder encapsula la mayoría de los métodos de StringBuilder y StringBuffer.

AbstractStringBuilder


Variables miembro y métodos de construcción.


char[] value;

int count;

AbstractStringBuilder() {
}

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

AbstractStringBuilder también define una matriz char [] para almacenar la cadena, pero no se modifica con final , por lo que la cadena es variable. Al mismo tiempo, también se define una variable global cout de tipo int . Cuando la configuración puede especificar la capacidad inicial de la capacidad , con el fin de obtener un mejor rendimiento.

Método de expansión


public void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > 0)
        ensureCapacityInternal(minimumCapacity);
}

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

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

private int hugeCapacity(int minCapacity) {
    if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

El método de expansión de AbstractStringBuilder es implementado por newCapacity () . En este método, la capacidad se expande al doble de la capacidad original (se usa la operación de cambio en lugar de *) y se agrega 2. Si la capacidad expandida es menor que la capacidad especificada, establezca minCapacity en la última capacidad. Si el valor máximo de Integer ( 0x7fffffff ) menos 8 es menor que la última capacidad, llame al método hugeCapacity () . Para determinar si se desborda, arroje una excepción OutOfMemoryError si se desborda; de lo contrario, si la nueva capacidad es mayor que el valor máximo de Integer menos 8, establezca la capacidad en minCapacity , de lo contrario, ajústelo al valor máximo de Integer menos 8. Si el valor máximo de Integer ( 0x7fffffff ) menos 8 es mayor que la última capacidad, de lo contrario, la capacidad se establece en newCapacity .

método append ()


public AbstractStringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}

public AbstractStringBuilder append(String str) {
	if (str == null)
		return appendNull();
	int len = str.length();
	ensureCapacityInternal(count + len);
	str.getChars(0, len, value, count);
	count += len;
	return this;
}

private AbstractStringBuilder appendNull() {
	int c = count;
	ensureCapacityInternal(c + 4);
	final char[] value = this.value;
	value[c++] = 'n';
	value[c++] = 'u';
	value[c++] = 'l';
	value[c++] = 'l';
	count = c;
	return this;
}

El método append () es el método más utilizado de StringBuilder y StringBuffer, sobrecargando muchos métodos para agregar cadenas. El primer método append () convierte el objeto obj al tipo String y lo agrega a través del método valueOf () del String . El segundo append () método, si str es nula , entonces la llamada () appendNull método, primero de expansión ( capacidad bruta plus 4 ), entonces un adicional 'n-' , 'U' , 'L' , 'L' de los cuatro Carácter Si str no es nulo , primera expansión ( capacidad bruta más la longitud de la cadena ), entonces llamar Cadena de getChars () método, str añade a la char [] array valor al final, y, finalmente, la capacidad deCapacidad original + longitud de cadena , devuelve el objeto en sí, para que pueda llamar continuamente al método append () .

StringBuilder


Método de construcción


public StringBuilder() {
	super(16);
}

public StringBuilder(int capacity) {
	super(capacity);
}

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

Se puede ver que la capacidad predeterminada de StringBuilder es 16 , y su capacidad inicial también se puede especificar. Si asigna un objeto StringBuilder con una cadena, la capacidad de StringBuiler en este momento es la longitud de la cadena actual más 16 .

método append ()


@Override
public StringBuilder append(Object obj) {
	return append(String.valueOf(obj));
}

@Override
public StringBuilder append(String str) {
	super.append(str);
	return this;
}

El método append () en StringBuilder es un método para reescribir la clase abstracta AbstractStringBuilder , sobrecargando muchos métodos append () . La implementación de sus métodos es llamar al método append () de la clase padre AbstractStringBuilder . Cuando varios subprocesos acceden a este método, porque el recuento es una variable global, cuando varios subprocesos ejecutan el recuento + = len , habrá problemas con el valor del recuento, lo que provocará una inconsistencia con el real, por lo que el subproceso StringBuilder no es seguro .

Método toString ()


@Override
public String toString() {
	// Create a copy, don't share the array
	return new String(value, 0, count);
}

StringBuilder reescribir el objeto 's toString () métodos, nueva nueva Stirng () llama internamente las matrices de copyOfRange () método ( copia en profundidad ), devuelve un nuevo objeto String, no un espacio de memoria compartida con el objeto original.

StringBuffer


Variables miembro y métodos de construcción.


private transient char[] toStringCache;

public StringBuffer() {
	super(16);
}

public StringBuffer(int capacity) {
	super(capacity);
}

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

Cuando se construye, el StringBuffer y StringBuilder como predeterminado tamaño de la capacidad de 16 , puede especificar la capacidad inicial. La única diferencia es que StringBuffer define una matriz char [] para StringCache decorada con transitorios . palabra clave transitoria , al modificar una variable, la variable no se puede serializar.

método append ()


@Override
public synchronized StringBuffer append(Object obj) {
	toStringCache = null;
	super.append(String.valueOf(obj));
	return this;
}

@Override
public synchronized StringBuffer append(String str) {
	toStringCache = null;
	super.append(str);
	return this;
}

Se puede ver que StringBuffer y StringBuilder reescriben el método append () de la clase padre de AbstractStringBuilder , implementan internamente el método append () de la clase padre y sobrecargan muchos métodos append () . La única diferencia es que cada vez que StringBuffer modifica los datos, la matriz char [] toStringCache se configurará como vacía, y cada método de StringBuffer está precedido por la palabra clave sincronizada . Cuando varios subprocesos acceden a este método, un subproceso está accediendo a este método, y otros subprocesos se bloquearán y no podrán acceder a este método. Solo cuando este subproceso ejecuta este método y libera el bloqueo, otros subprocesos pueden acceder a este método, por lo que StringBuffer Es seguro para el hilo .

Método toString ()


@Override
public synchronized String toString() {
	if (toStringCache == null) {
		toStringCache = Arrays.copyOfRange(value, 0, count);
	}
	return new String(toStringCache, true);
}

String(char[] value, boolean share) {
	// assert share : "unshared not supported";
	this.value = value;
}

StringBuffer 's toString () método y StringBuilder no es lo mismo. Si toStringCache es nulo , primero se almacena en caché y finalmente devuelve un objeto String . StringBuffer la nueva cadena () una interna no llame Arrays de copyOfRange () método, simplemente asignación array ( copia superficial ), copia los elementos para ahorrar tiempo.

Resumen


  • String es una cadena inmutable , StringBuffer y StringBuilder son cadenas variables .
  • StringBuilder no es seguro para subprocesos y es rápido , y StringBuffer es seguro para subprocesos y lento .
  • La mayoría de los métodos de StringBuilder y StringBuffer llaman a la implementación de la clase padre AbstractStringBuilder. El mecanismo de expansión es aumentar primero la capacidad a 2 veces la capacidad original más 2 . La capacidad máxima es el valor máximo de Integer (0x7fffffff) menos 8 .
  • La capacidad predeterminada de StringBuilder y StringBuffer son 16 , puede especificar el tamaño al crear StringBuffer y StringBuilder, para evitar el crecimiento automático cuando la capacidad no es suficiente para mejorar el rendimiento.
Publicado 131 artículos originales · ganó 12 · 60,000 vistas +

Supongo que te gusta

Origin blog.csdn.net/JavaDestiny/article/details/100710981
Recomendado
Clasificación