JavaRush /Java Blog /Random-IT /Stringhe in Java (classe java.lang.String)
Viacheslav
Livello 3

Stringhe in Java (classe java.lang.String)

Pubblicato nel gruppo Random-IT

introduzione

Il percorso di un programmatore è un processo complesso e lungo. E nella maggior parte dei casi inizia con un programma che visualizza Hello World sullo schermo. Java non fa eccezione (vedi Lezione: L'applicazione "Hello World!" ). Come possiamo vedere, il messaggio viene emesso utilizzando System.out.println("Hello World!"); Se guardi l'API Java, il metodo System.out.println accetta String come parametro di input . Questo tipo di dati verrà discusso.

Stringa come sequenza di caratteri

In realtà, String tradotto dall'inglese è una stringa. Esatto, il tipo String rappresenta una stringa di testo. Cos'è una stringa di testo? Una stringa di testo è una sorta di sequenza ordinata di caratteri che si susseguono. Il simbolo è char. Sequenza – sequenza. Quindi sì, assolutamente corretto, String è un'implementazione di java.lang.CharSequence. E se guardi all'interno della classe String stessa, al suo interno non c'è altro che un array di caratteri: private final char value[]; ha java.lang.CharSequenceun contratto abbastanza semplice:
Stringhe in Java (classe java.lang.String) - 1
Abbiamo un metodo per ottenere il numero di elementi, ottenere un elemento specifico e ottenere un insieme di elementi + il metodo toString stesso, che restituirà questo) È più interessante comprendere i metodi che ci sono arrivati ​​in Java 8, e questo è : chars()e codePoints() ricorda dal Tutorial di Oracle “ Primitive Data” Types " che char è single 16-bit Unicode character. Cioè, essenzialmente char è solo un tipo grande la metà di un int (32 bit) che rappresenta i numeri da 0 a 65535 (vedi valori decimali nella tabella ASCII ). Cioè, se lo desideriamo, possiamo rappresentare char come int. E Java 8 ne ha approfittato. A partire dalla versione 8 di Java, abbiamo IntStream , uno stream per lavorare con gli int primitivi. Pertanto, in charSequence è possibile ottenere un IntStream che rappresenta sia caratteri che codePoint. Prima di passare a loro, vedremo un esempio per mostrare la comodità di questo approccio. Usiamo il compilatore Java online Tutorialspoint ed eseguiamo il codice:
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
Ora puoi ottenere una serie di simboli unici in questo modo semplice.

CodePoint

Quindi, abbiamo visto i caratteri. Ora non è chiaro che tipo di punti di codice siano questi. Il concetto di codePoint è apparso perché quando è apparso Java, 16 bit (mezzo int) erano sufficienti per codificare un carattere. Pertanto, il carattere in Java è rappresentato nel formato UTF-16 (specifica "Unicode 88"). Successivamente è apparso Unicode 2.0, il cui concetto era quello di rappresentare un carattere come una coppia surrogata (2 caratteri). Ciò ci ha permesso di espandere la gamma di valori possibili fino a un valore int. Per maggiori dettagli, vedere StackOverflow: " Confronto di un carattere con un punto di codice? " UTF-16 è menzionato anche nel JavaDoc for Character . Lì, nel JavaDoc, si dice che: È 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). abbastanza difficile (e forse anche impossibile) riprodurlo negli alfabeti standard. Ma i simboli non finiscono con lettere e numeri. In Giappone hanno inventato qualcosa di così difficile da codificare come emoji: il linguaggio degli ideogrammi e delle emoticon. Su Wikipedia c'è un articolo interessante a riguardo: “ Emoji ”. Troviamo un esempio di emoji, ad esempio questo: “ Emoji Ghost ”. Come possiamo vedere, anche lì è indicato lo stesso codePoint (valore = U+1F47B). È indicato in formato esadecimale. Se convertiamo in un numero decimale, otteniamo 128123. Sono più di 16 bit consentiti (ovvero più di 65535). Copiamolo:
Stringhe in Java (classe java.lang.String) - 2
Sfortunatamente, la piattaforma JavaRush non supporta tali caratteri nel testo. Pertanto, nell'esempio seguente sarà necessario inserire un valore in String. Pertanto, ora capiremo un semplice test:
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
}
Come puoi vedere, in questo caso 1 codePoint vale 2 caratteri. Questa è la magia.

Carattere

Come abbiamo visto sopra, le stringhe in Java sono costituite da char. Un tipo primitivo ti consente di memorizzare un valore, ma un wrapper java.lang.Charactersu un tipo primitivo ti consente di fare molte cose utili con questo simbolo. Ad esempio, possiamo convertire una stringa in maiuscolo:
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));
}
Bene, varie cose interessanti: isAlphabetic(), isLetter(), isSpaceChar(), isDigit(), isUpperCase(), isMirrored()(ad esempio, parentesi. '(' ha un'immagine speculare ')').

Piscina di corde

Le stringhe in Java sono immutabili, cioè costanti. Ciò è indicato anche nel JavaDoc della classe java.lang.String stessa . In secondo luogo, e anche molto importante, le stringhe possono essere specificate come valori letterali:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
Cioè, qualsiasi stringa tra virgolette, come affermato sopra, è in realtà un oggetto. E questo fa sorgere la domanda: se usiamo stringhe così spesso e spesso possono essere le stesse (ad esempio, il testo "Errore" o "Con successo"), esiste un modo per assicurarsi che le stringhe non vengano create ogni volta? A proposito, abbiamo ancora Maps, dove la chiave può essere una stringa. Quindi sicuramente non possiamo avere le stesse stringhe come oggetti diversi, altrimenti non saremo in grado di ottenere l'oggetto dalla mappa. Gli sviluppatori Java hanno pensato, pensato e hanno ideato String Pool. Questo è un luogo in cui vengono archiviate le stringhe, puoi chiamarlo cache di stringhe. Non tutte le righe finiscono lì, ma solo le righe specificate nel codice da un valore letterale. Puoi aggiungere tu stesso una linea alla piscina, ma ne parleremo più avanti. Quindi, in memoria abbiamo questa cache da qualche parte. Una domanda giusta: dove si trova questa piscina? La risposta a questa domanda può essere trovata su StackOverflow: " Dove si trova il pool costante String di Java, l'heap o lo stack?" " Si trova nella memoria Heap, in una speciale area del pool di costanti di runtime. Il pool di costanti di runtime viene allocato quando una classe o un'interfaccia viene creata dalla macchina virtuale dall'area del metodo , un'area speciale nell'heap a cui hanno accesso tutti i thread all'interno della Java Virtual Machine. Cosa ci offre lo String pool? Ciò presenta diversi vantaggi:
  • Non verranno creati oggetti dello stesso tipo
  • Il confronto per riferimento è più veloce del confronto carattere per carattere tramite uguaglianza
Ma cosa succede se vogliamo mettere l'oggetto creato in questa cache? Poi abbiamo un metodo speciale: String.intern Questo metodo aggiunge una stringa allo String Pool. Vale la pena notare che questa non è solo una sorta di cache sotto forma di array (come per gli interi). Il metodo interno è specificato come "nativo". Ciò significa che il metodo stesso è implementato in un altro linguaggio (principalmente C++). Ai metodi Java di base possono essere applicate diverse altre ottimizzazioni a livello JVM. In generale, qui accadrà la magia. È interessante leggere il seguente post sugli stagisti: https://habr.com/post/79913/#comment_2345814 E sembra una buona idea. Ma come ci influenzerà questo? Ma avrà davvero un impatto)
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal");
    System.out.println(test == test2);
}
Come puoi vedere, le linee sono le stesse, ma il risultato sarà falso. E tutto perché == confronta non in base al valore, ma in base al riferimento. Ed è così che funziona:
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test == test2);
}
Tieni presente che creeremo comunque una nuova stringa. Cioè, intern ci restituirà una stringa dalla cache, ma la stringa originale che abbiamo cercato nella cache verrà eliminata per la pulizia, perché nessun altro sa di lui. Questo è chiaramente un consumo inutile di risorse =( Pertanto, dovresti sempre confrontare le stringhe usando uguali per evitare il più possibile errori improvvisi e difficili da rilevare.
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
Uguale esegue un confronto tra stringhe carattere per carattere.

Concatenazione

Come ricordiamo, è possibile aggiungere linee. E come ricordiamo, le nostre corde sono immutabili. Allora come funziona? Esatto, viene creata una nuova riga, che consiste nei simboli degli oggetti aggiunti. Esistono milioni di versioni di come funziona la concatenazione plus. Alcune persone pensano che ogni volta ci sarà un nuovo oggetto, altri pensano che ci sarà qualcos'altro. Ma solo una persona può avere ragione. E quel qualcuno è il compilatore Javac. Usiamo il servizio di compilazione online ed eseguiamo:
public class HelloWorld {

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

}
Adesso salviamolo come archivio zip, estraiamolo in una directory ed eseguiamo: javap –c HelloWorld E qui scopriamo tutto:
Stringhe in Java (classe java.lang.String) - 3
In un ciclo, ovviamente, è meglio eseguire tu stesso la concatenazione tramite StringBuilder. E non per qualche tipo di magia, ma in modo che StringBuilder venga creato prima del ciclo e nel ciclo stesso si verifichi solo l'aggiunta. A proposito, c'è un'altra cosa interessante qui. C'è un articolo eccellente: " Elaborazione delle stringhe in Java. Parte I: String, StringBuffer, StringBuilder ." Tante informazioni utili nei commenti. Ad esempio, si specifica che quando si concatena una vista, new StringBuilder().append()...toString()è attiva l'ottimizzazione intrinseca, regolata dall'opzione -XX:+OptimizeStringConcat, che è abilitata per impostazione predefinita. intrinseco - tradotto come "interno". La JVM gestisce queste cose in un modo speciale, elaborandole come native, solo senza i costi aggiuntivi di JNI. Per saperne di più: " Metodi intrinseci in HotSpot VM ".

StringBuilder e StringBuffer

Come abbiamo visto sopra, StringBuilder è uno strumento molto utile. Le stringhe sono immutabili, cioè immutabile. E voglio piegarlo. Pertanto, ci vengono fornite 2 classi per aiutarci: StringBuilder e StringBuffer. La differenza principale tra i due è che StringBuffer è stato introdotto in JDK1.0, mentre StringBuilder è arrivato in Java 1.5 come versione non sincronizzata di StringBuffer per eliminare il maggiore sovraccarico della sincronizzazione dei metodi non necessaria. Entrambe queste classi sono implementazioni della classe astratta AbstractStringBuilder - Una sequenza mutabile di caratteri. Al suo interno è memorizzato un array di accessi, che viene espanso secondo la regola: value.length * 2 + 2. Per impostazione predefinita, la dimensione (capacità) di StringBuilder è 16.

Paragonabile

Le stringhe sono comparabili, ad es. implementare il metodo compareTo. Questo viene fatto utilizzando il confronto carattere per carattere. È interessante notare che la lunghezza minima viene selezionata tra due stringhe e su di essa viene eseguito un loop. Pertanto, compareTo restituirà la differenza tra i valori int dei primi caratteri non corrispondenti fino alla lunghezza della stringa minima oppure restituirà la differenza tra le lunghezze delle stringhe se tutti i caratteri corrispondono entro la lunghezza minima della stringa. Questo confronto si chiama “lessicografico”.

Lavorare con stringhe Java

String ha molti metodi utili:
Stringhe in Java (classe java.lang.String) - 4
Esistono molti compiti per lavorare con le stringhe. Ad esempio, su Coding Bat . C'è anche un corso su Coursera: " Algorithms on Strings ".

Conclusione

Anche una breve panoramica di questa classe occupa una quantità impressionante di spazio. E non è tutto. Consiglio vivamente di guardare il rapporto di JPoint 2015: Alexey Shipilev - Catechismo java.lang.String
#Viacheslav
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION