JavaRush /Blog Java /Random-ES /Cadenas en Java (clase java.lang.String)
Viacheslav
Nivel 3

Cadenas en Java (clase java.lang.String)

Publicado en el grupo Random-ES

Introducción

El camino de un programador es un proceso largo y complejo. Y en la mayoría de los casos comienza con un programa que muestra Hola Mundo en la pantalla. Java no es una excepción (consulte la Lección: La aplicación "¡Hola mundo!" ). Como podemos ver, el mensaje se genera utilizando System.out.println("Hello World!"); Si observa la API de Java, el método System.out.println toma String como parámetro de entrada . Este tipo de datos serán discutidos.

Cadena como una secuencia de caracteres.

En realidad, String traducido del inglés es una cadena. Así es, el tipo String representa una cadena de texto. ¿Qué es una cadena de texto? Una cadena de texto es una especie de secuencia ordenada de caracteres que se suceden entre sí. El símbolo es carbón. Secuencia – secuencia. Entonces sí, absolutamente correcto, String es una implementación de java.lang.CharSequence. Y si miras dentro de la clase String, dentro de ella no hay nada más que una serie de caracteres: private final char value[]; Tiene java.lang.CharSequenceun contrato bastante simple:
Cadenas en Java (clase java.lang.String) - 1
Tenemos un método para obtener la cantidad de elementos, obtener un elemento específico y obtener un conjunto de elementos + el método toString en sí, que devolverá esto) Es más interesante comprender los métodos que nos llegaron en Java 8, y este es : chars()y codePoints() recuerde del tutorial de Oracle " Tipos de datos primitivos" que es char single 16-bit Unicode character. Es decir, esencialmente char es solo un tipo de la mitad del tamaño de un int (32 bits) que representa números del 0 al 65535 (ver valores decimales en la tabla ASCII ). Es decir, si lo deseamos, podemos representar char como int. Y Java 8 aprovechó esto. A partir de la versión 8 de Java, tenemos IntStream , una secuencia para trabajar con entradas primitivas. Por lo tanto, en charSequence es posible obtener un IntStream que represente caracteres o puntos de código. Antes de pasar a ellos, veremos un ejemplo para mostrar la conveniencia de este enfoque. Usemos el compilador Java en línea Tutorialspoint y ejecutemos el código:
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
Ahora puedes obtener una serie de símbolos únicos de esta sencilla forma.

Puntos de código

Entonces, vimos sobre los caracteres. Ahora no está claro qué tipo de puntos de código son estos. El concepto de codePoint apareció porque cuando apareció Java, 16 bits (medio int) eran suficientes para codificar un carácter. Por lo tanto, char en Java se representa en formato UTF-16 (especificación "Unicode 88"). Más tarde apareció Unicode 2.0, cuyo concepto era representar un carácter como un par sustituto (2 caracteres). Esto nos permitió ampliar el rango de valores posibles a un valor int. Para obtener más detalles, consulte stackoverflow: "¿ Comparar un carácter con un punto de código? " UTF-16 también se menciona en JavaDoc para Character . Allí, en el JavaDoc, se dice que: Es In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogates range, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF). bastante difícil (y tal vez incluso imposible) reproducir esto en alfabetos estándar. Pero los símbolos no terminan en letras y números. En Japón se les ocurrió algo tan difícil de codificar como los emoji, el lenguaje de los ideogramas y los emoticones. Hay un artículo interesante sobre esto en Wikipedia: “ Emoji ”. Busquemos un ejemplo de emoji, por ejemplo este: “ Emoji Fantasma ”. Como podemos ver, allí incluso se indica el mismo codePoint (valor = U+1F47B). Se indica en formato hexadecimal. Si convertimos a un número decimal, obtenemos 128123. Esto es más de los 16 bits permitidos (es decir, más de 65535). Copiémoslo:
Cadenas en Java (clase java.lang.String) - 2
Lamentablemente, la plataforma JavaRush no admite este tipo de caracteres en el texto. Por lo tanto, en el siguiente ejemplo deberá insertar un valor en String. Por tanto, ahora entenderemos una prueba sencilla:
public static void main(String []args){
	    String emojiString = "Вставте сюда эмоджи через ctrl+v";
	    //На один emojiString приходится 2 чара (т.к. не влезает в 16 бит)
	    System.out.println(emojiString.codePoints().count()); //1
	    System.out.println(emojiString.chars().count()); //2
}
Como puede ver, en este caso 1 codePoint equivale a 2 caracteres. Ésta es la magia.

Personaje

Como vimos anteriormente, las cadenas en Java constan de caracteres. Un tipo primitivo le permite almacenar un valor, pero un contenedor java.lang.Charactersobre un tipo primitivo le permite hacer muchas cosas útiles con este símbolo. Por ejemplo, podemos convertir una cadena a mayúsculas:
public static void main(String[] args) {
    String line = "организация объединённых наций";
    char[] chars = line.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        if (i == 0 || chars[i - 1] == ' ') {
            chars[i] = Character.toUpperCase(chars[i]);
        }
    }
    System.out.println(new String(chars));
}
Bueno, varias cosas interesantes: isAlphabetic(), isLetter(), isSpaceChar(), isDigit(), isUpperCase(), isMirrored()(por ejemplo, corchetes. '(' tiene una imagen reflejada ')').

Piscina de cuerdas

Las cadenas en Java son inmutables, es decir, constantes. Esto también se indica en el JavaDoc de la propia clase java.lang.String . En segundo lugar, y también muy importante, las cadenas se pueden especificar como literales:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
Es decir, cualquier cadena entrecomillada, como se indicó anteriormente, es en realidad un objeto. Y esto plantea la pregunta: si usamos cadenas con tanta frecuencia y a menudo pueden ser las mismas (por ejemplo, el texto "Error" o "Con éxito"), ¿hay alguna manera de asegurarnos de que las cadenas no se creen cada vez? Por cierto, todavía tenemos Maps, donde la clave puede ser una cadena. Entonces definitivamente no podemos tener las mismas cadenas como objetos diferentes, de lo contrario no podremos obtener el objeto del Mapa. Los desarrolladores de Java pensaron, pensaron y crearon String Pool. Este es un lugar donde se almacenan las cadenas, puede llamarlo caché de cadenas. No todas las líneas terminan allí, sino sólo las líneas especificadas en el código mediante un literal. Puedes agregar una línea al grupo tú mismo, pero hablaremos de eso más adelante. Entonces, en la memoria tenemos este caché en alguna parte. Una pregunta justa: ¿dónde está ubicada esta piscina? La respuesta a esto se puede encontrar en stackoverflow: “¿ Dónde vive el grupo constante de cadenas de Java, el montón o la pila? " Está ubicado en la memoria Heap, en un área de grupo constante de tiempo de ejecución especial. El grupo constante de tiempo de ejecución se asigna cuando la máquina virtual crea una clase o interfaz desde el área de método , un área especial en el montón a la que todos los subprocesos dentro de la máquina virtual Java tienen acceso. ¿Qué nos aporta String pool? Esto tiene varias ventajas:
  • No se crearán objetos del mismo tipo.
  • La comparación por referencia es más rápida que la comparación carácter por carácter mediante iguales
Pero ¿qué pasa si queremos poner el objeto creado en este caché? Luego, tenemos un método especial: String.intern Este método agrega una cadena al String Pool. Vale la pena señalar que esto no es solo una especie de caché en forma de matriz (como ocurre con los enteros). El método interno se especifica como "nativo". Esto significa que el método en sí está implementado en otro lenguaje (principalmente C++). En el caso de los métodos básicos de Java, se les pueden aplicar otras optimizaciones a nivel de JVM. En general, aquí sucederá magia. Es interesante leer el siguiente post sobre becario: https://habr.com/post/79913/#comment_2345814 Y me parece una buena idea. Pero ¿cómo nos afectará esto? Pero realmente tendrá un impacto)
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal");
    System.out.println(test == test2);
}
Como puedes ver, las líneas son iguales, pero el resultado será falso. Y todo porque == compara no por valor, sino por referencia. Y así es como funciona:
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test == test2);
}
Solo tenga en cuenta que seguiremos creando una nueva cadena. Es decir, el interno nos devolverá una Cadena del caché, pero la Cadena original que buscamos en el caché será desechada para su limpieza, porque nadie más sabe de él. Esto es claramente un consumo innecesario de recursos =( Por lo tanto, siempre debes comparar cadenas usando iguales para evitar en la medida de lo posible errores repentinos y difíciles de detectar.
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
Equals realiza una comparación de cadenas carácter por carácter.

Concatenación

Como recordamos, se pueden agregar líneas. Y como recordamos, nuestras cuerdas son inmutables. Entonces, ¿cómo funciona? Así es, se crea una nueva línea que consta de símbolos de los objetos que se agregan. Hay un millón de versiones de cómo funciona la concatenación plus. Algunas personas piensan que cada vez habrá un objeto nuevo, otras piensan que habrá algo más. Pero sólo una persona puede tener razón. Y ese alguien es el compilador javac. Usemos el servicio de compilación en línea y ejecutemos:
public class HelloWorld {

    public static void main(String[] args) {
        String helloMessage = "Hello, ";
        String target = "World";
        System.out.println(helloMessage + target);
    }

}
Ahora guardemos esto como un archivo zip, lo extraigamos a un directorio y ejecutemos: javap –c HelloWorld Y aquí descubrimos todo:
Cadenas en Java (clase java.lang.String) - 3
En un bucle, por supuesto, es mejor realizar la concatenación usted mismo a través de StringBuilder. Y no por algún tipo de magia, sino para que StringBuilder se cree antes del ciclo, y en el ciclo en sí solo se agrega. Por cierto, aquí hay otra cosa interesante. Hay un artículo excelente: “ Procesamiento de cadenas en Java. Parte I: Cadena, StringBuffer, StringBuilder ." Mucha información útil en los comentarios. Por ejemplo, se especifica que al concatenar una vista, new StringBuilder().append()...toString()está vigente la optimización intrínseca, regulada por la opción -XX:+OptimizeStringConcat, que está habilitada por defecto. intrínseco - traducido como "interno". La JVM maneja estas cosas de una manera especial, procesándolas como nativas, solo que sin los costos adicionales de JNI. Leer más: " Métodos intrínsecos en HotSpot VM ".

StringBuilder y StringBuffer

Como vimos anteriormente, StringBuilder es una herramienta muy útil. Las cadenas son inmutables, es decir. inmutable. Y quiero doblarlo. Por lo tanto, contamos con 2 clases para ayudarnos: StringBuilder y StringBuffer. La principal diferencia entre los dos es que StringBuffer se introdujo en JDK1.0, mientras que StringBuilder llegó en Java 1.5 como una versión no sincronizada de StringBuffer para eliminar la mayor sobrecarga de la sincronización innecesaria de métodos. Ambas clases son implementaciones de la clase abstracta AbstractStringBuilder: una secuencia mutable de caracteres. En el interior se almacena una matriz de accesos, que se expande de acuerdo con la regla: valor.longitud * 2 + 2. De forma predeterminada, el tamaño (capacidad) de StringBuilder es 16.

Comparable

Las cuerdas son comparables, es decir. implementar el método compareTo. Esto se hace mediante comparación carácter por carácter. Curiosamente, la longitud mínima se selecciona entre dos cadenas y se ejecuta un bucle sobre ella. Por lo tanto, compareTo devolverá la diferencia entre los valores int de los primeros caracteres no coincidentes hasta la longitud de cadena más pequeña, o devolverá la diferencia entre las longitudes de las cadenas si todos los caracteres coinciden dentro de la longitud mínima de la cadena. Esta comparación se llama "lexicográfica".

Trabajar con cadenas de Java

String tiene muchos métodos útiles:
Cadenas en Java (clase java.lang.String) - 4
Hay muchas tareas para trabajar con cuerdas. Por ejemplo, en Coding Bat . También hay un curso en Coursera: " Algoritmos sobre cadenas ".

Conclusión

Incluso una breve descripción general de esta clase ocupa una cantidad impresionante de espacio. Y eso no es todo. Recomiendo encarecidamente ver el informe de JPoint 2015: Alexey Shipilev - Catecismo java.lang.String
#viacheslav
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION