Java Language
stringhe
Ricerca…
introduzione
Le stringhe ( java.lang.String
) sono pezzi di testo memorizzati nel tuo programma. Le stringhe non sono un tipo di dati primitivi in Java , tuttavia, sono molto comuni nei programmi Java.
In Java, le stringhe sono immutabili, il che significa che non possono essere modificate. (Fare clic qui per una spiegazione più approfondita dell'immutabilità).
Osservazioni
Poiché le stringhe Java sono immutabili , tutti i metodi che manipolano una String
restituiranno un nuovo oggetto String
. Non cambiano la String
originale. Ciò include i metodi di sottostringa e di sostituzione che i programmatori C e C ++ si aspettano di modificare l'oggetto String
destinazione.
Utilizzare un StringBuilder
invece di String
se si desidera concatenare più di due oggetti String
cui valori non possono essere determinati in fase di compilazione. Questa tecnica è più performante rispetto alla creazione di nuovi oggetti String
e concatenandoli perché StringBuilder
è modificabile.
StringBuffer
può anche essere utilizzato per concatenare oggetti String
. Tuttavia, questa classe è meno performante perché è progettata per essere thread-safe e acquisisce un mutex prima di ogni operazione. Dal momento che non si ha quasi mai bisogno di thread-safe quando si concatenano le stringhe, è meglio usare StringBuilder
.
Se puoi esprimere una concatenazione di stringhe come una singola espressione, allora è meglio usare l'operatore +
. Il compilatore Java convertirà un'espressione contenente +
concatenazioni in una sequenza efficiente di operazioni utilizzando String.concat(...)
o StringBuilder
. Il consiglio di usare StringBuilder
esplicitamente si applica solo quando la concatenazione coinvolge più espressioni.
Non memorizzare informazioni sensibili nelle stringhe. Se qualcuno è in grado di ottenere un dump della memoria della tua applicazione in esecuzione, sarà in grado di trovare tutti gli oggetti String
esistenti e leggerne il contenuto. Ciò include oggetti String
non raggiungibili e in attesa di garbage collection. Se questo è un problema, è necessario cancellare i dati sensibili delle stringhe non appena ne hai finito. Non puoi farlo con gli oggetti String
poiché sono immutabili. Pertanto, è consigliabile utilizzare un oggetto char[]
per conservare dati di carattere sensibili e pulirli (ad es. Sovrascriverli con caratteri '\000'
) una volta terminato.
Tutte le istanze String
vengono create nell'heap, anche le istanze corrispondenti ai valori letterali stringa. La particolarità dei valori letterali delle stringhe è che la JVM garantisce che tutti i letterali uguali (ovvero che siano costituiti dagli stessi caratteri) siano rappresentati da un singolo oggetto String
(questo comportamento è specificato in JLS). Questo è implementato dai caricatori di classi JVM. Quando un programma di caricamento classe carica una classe, analizza i valori letterali stringa utilizzati nella definizione di classe, ogni volta che ne rileva uno, controlla se esiste già un record nel pool di stringhe per questo valore letterale (utilizzando il valore letterale come chiave) . Se esiste già una voce per il letterale, viene utilizzato il riferimento a un'istanza di String
archiviata come coppia per quel letterale. Altrimenti, viene creata una nuova istanza String
e un riferimento all'istanza viene archiviato per il letterale (utilizzato come chiave) nel pool di stringhe. (Vedi anche internamento stringa ).
Il pool di stringhe viene tenuto nell'heap Java ed è soggetto alla normale garbage collection.
Nelle versioni di Java precedenti a Java 7, il pool di stringhe era contenuto in una parte speciale dello heap nota come "PermGen". Questa parte è stata raccolta solo occasionalmente.
In Java 7, il pool di stringhe è stato spostato da "PermGen".
Si noti che i valori letterali stringa sono implicitamente raggiungibili da qualsiasi metodo che li utilizza. Ciò significa che gli oggetti String
corrispondenti possono essere raccolti solo se il codice stesso è garbage collector.
Fino a Java 8, gli oggetti String
vengono implementati come un array di dati UTF-16 (2 byte per carattere). C'è una proposta in Java 9 per implementare String
come array di byte con un campo flag di codifica per notare se la stringa è codificata come byte (LATIN-1) o chars (UTF-16).
Confronto di stringhe
Per confrontare le stringhe per l'uguaglianza, è necessario utilizzare i metodi equals
o equalsIgnoreCase
dell'oggetto String.
Ad esempio, il seguente snippet determinerà se le due istanze di String
sono uguali su tutti i caratteri:
String firstString = "Test123";
String secondString = "Test" + 123;
if (firstString.equals(secondString)) {
// Both Strings have the same content.
}
Questo esempio li confronterà, indipendentemente dal loro caso:
String firstString = "Test123";
String secondString = "TEST123";
if (firstString.equalsIgnoreCase(secondString)) {
// Both Strings are equal, ignoring the case of the individual characters.
}
Si noti che equalsIgnoreCase
non consente di specificare un Locale
. Ad esempio, se si confrontano le due parole "Taki"
e "TAKI"
in inglese sono uguali; tuttavia, in turco sono diversi (in turco, il minuscolo I
è ı
). Per casi come questo, la conversione di entrambe le stringhe in minuscolo (o maiuscolo) con Locale
e il confronto con gli equals
è la soluzione.
String firstString = "Taki";
String secondString = "TAKI";
System.out.println(firstString.equalsIgnoreCase(secondString)); //prints true
Locale locale = Locale.forLanguageTag("tr-TR");
System.out.println(firstString.toLowerCase(locale).equals(
secondString.toLowerCase(locale))); //prints false
Non utilizzare l'operatore == per confrontare le stringhe
A meno che tu non possa garantire che tutte le stringhe siano state internate (vedi sotto), non devi usare gli operatori ==
o !=
Per confrontare le stringhe. Questi operatori verificano effettivamente i riferimenti e, poiché più oggetti String
possono rappresentare la stessa stringa, è possibile fornire una risposta errata.
Utilizzare invece il String.equals(Object)
, che confronterà gli oggetti String in base ai loro valori. Per una spiegazione dettagliata, fai riferimento a Pitfall: usando == per confrontare le stringhe .
Confronto tra stringhe in un'istruzione switch
A partire da Java 1.7, è possibile confrontare una variabile String a valori letterali in un'istruzione switch
. Assicurati che String non sia nullo, altrimenti genererà sempre una NullPointerException
. I valori vengono confrontati usando String.equals
, ovvero case sensitive.
String stringToSwitch = "A";
switch (stringToSwitch) {
case "a":
System.out.println("a");
break;
case "A":
System.out.println("A"); //the code goes here
break;
case "B":
System.out.println("B");
break;
default:
break;
}
Confronto di stringhe con valori costanti
Quando si confronta una String
con un valore costante, è possibile inserire il valore costante sul lato sinistro degli equals
per garantire che non si otterrà NullPointerException
se l'altra stringa è null
.
"baz".equals(foo)
Mentre foo.equals("baz")
lancia una NullPointerException
se foo
è null
, "baz".equals(foo)
valuterà a false
.
Un'alternativa più leggibile consiste nell'utilizzare Objects.equals()
, che esegue un controllo nullo su entrambi i parametri: Objects.equals(foo, "baz")
.
( Nota: è discutibile se sia meglio evitare NullPointerExceptions
in generale, o lasciarli accadere e quindi correggere la causa principale, vedere qui e qui . Certamente, chiamare la strategia di evitamento "best practice" non è giustificabile).
Ordinamenti di stringhe
La classe String
implementa Comparable<String>
con il metodo String.compareTo
(come descritto all'inizio di questo esempio). Ciò rende l'ordinamento naturale degli oggetti String
ordine con distinzione tra maiuscole e minuscole. La classe String
fornisce una costante di Comparator<String>
chiamata CASE_INSENSITIVE_ORDER
adatta per l'ordinamento senza distinzione tra maiuscole e minuscole.
Confronto con stringhe internate
La specifica del linguaggio Java ( JLS 3.10.6 ) afferma quanto segue:
"Inoltre, una stringa letterale fa sempre riferimento alla stessa istanza della classe
String
poiché stringhe letterali o, più in generale, stringhe che rappresentano i valori delle espressioni costanti, vengono internate in modo da condividere istanze univoche, utilizzando il metodoString.intern
. "
Ciò significa che è possibile confrontare i riferimenti a due valori letterali stringa usando ==
. Inoltre, lo stesso vale per i riferimenti agli oggetti String
che sono stati prodotti utilizzando il metodo String.intern()
.
Per esempio:
String strObj = new String("Hello!");
String str = "Hello!";
// The two string references point two strings that are equal
if (strObj.equals(str)) {
System.out.println("The strings are equal");
}
// The two string references do not point to the same object
if (strObj != str) {
System.out.println("The strings are not the same object");
}
// If we intern a string that is equal to a given literal, the result is
// a string that has the same reference as the literal.
String internedStr = strObj.intern();
if (internedStr == str) {
System.out.println("The interned string and the literal are the same object");
}
Dietro le quinte, il meccanismo di interning mantiene una tabella hash che contiene tutte le stringhe internamente ancora raggiungibili . Quando si chiama intern()
su una String
, il metodo cerca l'oggetto nella tabella hash:
- Se la stringa viene trovata, tale valore viene restituito come stringa internata.
- Altrimenti, una copia della stringa viene aggiunta alla tabella hash e tale stringa viene restituita come stringa internata.
È possibile utilizzare l'interning per consentire il confronto delle stringhe utilizzando ==
. Tuttavia, ci sono problemi significativi con questo; vedi Pitfall - Le stringhe Internazionali in modo che tu possa usare == è una cattiva idea per i dettagli. Non è raccomandato nella maggior parte dei casi.
Modifica del caso di caratteri all'interno di una stringa
Il tipo String
fornisce due metodi per la conversione di stringhe tra maiuscole e minuscole:
-
toUpperCase
per convertire tutti i caratteri in maiuscolo -
toLowerCase
per convertire tutti i caratteri in lettere minuscole
Questi metodi restituiscono entrambe le stringhe convertite come nuove istanze String
: gli oggetti String
originali non vengono modificati perché String
è immutabile in Java. Vedi questo per saperne di più sull'immutabilità: Immutabilità di stringhe in Java
String string = "This is a Random String";
String upper = string.toUpperCase();
String lower = string.toLowerCase();
System.out.println(string); // prints "This is a Random String"
System.out.println(lower); // prints "this is a random string"
System.out.println(upper); // prints "THIS IS A RANDOM STRING"
I caratteri non alfabetici, come cifre e segni di punteggiatura, non sono influenzati da questi metodi. Si noti che questi metodi possono anche trattare in modo errato determinati caratteri Unicode in determinate condizioni.
Nota : questi metodi sono sensibili alle impostazioni internazionali e potrebbero produrre risultati imprevisti se utilizzati su stringhe che devono essere interpretate indipendentemente dalle impostazioni internazionali. Gli esempi sono identificatori di linguaggio di programmazione, chiavi di protocollo e tag HTML
.
Ad esempio, "TITLE".toLowerCase()
in una locale turca restituisce " tıtle
", dove ı (\u0131)
è il carattere LATIN SMALL LETTER DOTLESS I. Per ottenere risultati corretti per le stringhe insensibili della locale, passare Locale.ROOT
come parametro al metodo di conversione caso corrispondente (ad es toLowerCase(Locale.ROOT)
o toUpperCase(Locale.ROOT)
).
Anche se l'uso di Locale.ENGLISH
è corretto anche per la maggior parte dei casi, il modo in cui la lingua è invariata è Locale.ROOT
.
Un elenco dettagliato dei caratteri Unicode che richiedono un involucro speciale può essere trovato sul sito Web del Consorzio Unicode .
Modifica del caso di un carattere specifico all'interno di una stringa ASCII:
Per cambiare il caso di un carattere specifico di una stringa ASCII, è possibile utilizzare l'algoritmo seguente:
passi:
- Dichiara una stringa.
- Inserisci la stringa.
- Converti la stringa in un array di caratteri.
- Inserisci il carattere che deve essere cercato.
- Cerca il personaggio nell'array di caratteri.
- Se trovato, controlla se il carattere è in minuscolo o maiuscolo.
- Se è maiuscolo, aggiungi 32 al codice ASCII del carattere.
- Se Minuscolo, sottrai 32 dal codice ASCII del carattere.
- Cambia il carattere originale dall'array di caratteri.
- Converti nuovamente l'array di caratteri nella stringa.
Voilà, il caso del personaggio è cambiato.
Un esempio del codice dell'algoritmo è:
Scanner scanner = new Scanner(System.in);
System.out.println("Enter the String");
String s = scanner.next();
char[] a = s.toCharArray();
System.out.println("Enter the character you are looking for");
System.out.println(s);
String c = scanner.next();
char d = c.charAt(0);
for (int i = 0; i <= s.length(); i++) {
if (a[i] == d) {
if (d >= 'a' && d <= 'z') {
d -= 32;
} else if (d >= 'A' && d <= 'Z') {
d += 32;
}
a[i] = d;
break;
}
}
s = String.valueOf(a);
System.out.println(s);
Trovare una stringa all'interno di un'altra stringa
Per verificare se una particolare stringa a
è contenuta in una stringa b
oppure no, possiamo usare il metodo String.contains()
con la seguente sintassi:
b.contains(a); // Return true if a is contained in b, false otherwise
Il metodo String.contains()
può essere utilizzato per verificare se è possibile trovare un CharSequence
nella stringa. Il metodo cerca la stringa a
nella stringa b
in modo maiuscole e minuscole.
String str1 = "Hello World";
String str2 = "Hello";
String str3 = "helLO";
System.out.println(str1.contains(str2)); //prints true
System.out.println(str1.contains(str3)); //prints false
Per trovare la posizione esatta in cui una stringa inizia all'interno di un'altra stringa, utilizzare String.indexOf()
:
String s = "this is a long sentence";
int i = s.indexOf('i'); // the first 'i' in String is at index 2
int j = s.indexOf("long"); // the index of the first occurrence of "long" in s is 10
int k = s.indexOf('z'); // k is -1 because 'z' was not found in String s
int h = s.indexOf("LoNg"); // h is -1 because "LoNg" was not found in String s
Il metodo String.indexOf()
restituisce il primo indice di un char
o di una String
in un'altra String
. Il metodo restituisce -1
se non viene trovato.
Nota : il metodo String.indexOf()
è sensibile al maiuscolo / minuscolo.
Esempio di ricerca che ignora il caso:
String str1 = "Hello World";
String str2 = "wOr";
str1.indexOf(str2); // -1
str1.toLowerCase().contains(str2.toLowerCase()); // true
str1.toLowerCase().indexOf(str2.toLowerCase()); // 6
Ottenere la lunghezza di una stringa
Per ottenere la lunghezza di un oggetto String
, chiamare il metodo length()
su di esso. La lunghezza è uguale al numero di unità di codice UTF-16 (caratteri) nella stringa.
String str = "Hello, World!";
System.out.println(str.length()); // Prints out 13
Un char
in una stringa è il valore UTF-16. I codepoint Unicode i cui valori sono ≥ 0x1000 (ad esempio, la maggior parte degli emoji) utilizzano due posizioni char. Per contare il numero di punti di codice Unicode in una stringa, indipendentemente dal fatto che ciascun punto di codice rientri in un valore di char
UTF-16, è possibile utilizzare il metodo codePointCount
:
int length = str.codePointCount(0, str.length());
È inoltre possibile utilizzare un flusso di codepoint, come di Java 8:
int length = str.codePoints().count();
sottostringhe
String s = "this is an example";
String a = s.substring(11); // a will hold the string starting at character 11 until the end ("example")
String b = s.substring(5, 10); // b will hold the string starting at character 5 and ending right before character 10 ("is an")
String b = s.substring(5, b.length()-3); // b will hold the string starting at character 5 ending right before b' s lenght is out of 3 ("is an exam")
Le sottostringhe possono anche essere applicate per tagliare e aggiungere / sostituire caratteri nella sua stringa originale. Ad esempio, hai affrontato una data cinese contenente caratteri cinesi, ma vuoi memorizzarla come una stringa di data in formato well.
String datestring = "2015年11月17日"
datestring = datestring.substring(0, 4) + "-" + datestring.substring(5,7) + "-" + datestring.substring(8,10);
//Result will be 2015-11-17
Il metodo della sottostringa estrae un pezzo di una String
. Quando viene fornito un parametro, il parametro è l'inizio e il pezzo si estende fino alla fine della String
. Quando vengono dati due parametri, il primo parametro è il carattere di partenza e il secondo parametro è l'indice del carattere subito dopo la fine (il carattere dell'indice non è incluso). Un modo semplice per verificare è la sottrazione del primo parametro dal secondo dovrebbe fornire la lunghezza prevista della stringa.
Nelle versioni JDK <7u6 il metodo della substring
crea un'istanza di una String
che condivide lo stesso char[]
backup char[]
della String
originale e dispone dei campi di offset
e count
interni impostati per l'inizio e la lunghezza del risultato. Tale condivisione può causare perdite di memoria, che possono essere prevenute chiamando la new String(s.substring(...))
per forzare la creazione di una copia, dopo di che char[]
può essere garbage collection.
Da JDK 7u6 il metodo della substring
copia sempre l'intero array char[]
sottostante, rendendo la complessità lineare rispetto alla costante precedente ma garantendo l'assenza di perdite di memoria contemporaneamente.
Ottenere l'ennesimo carattere in una stringa
String str = "My String";
System.out.println(str.charAt(0)); // "M"
System.out.println(str.charAt(1)); // "y"
System.out.println(str.charAt(2)); // " "
System.out.println(str.charAt(str.length-1)); // Last character "g"
Per ottenere l'ennesimo carattere in una stringa, chiama semplicemente charAt(n)
su una String
, dove n
è l'indice del carattere che desideri recuperare
NOTA: l' indice n
inizia a 0
, quindi il primo elemento è n = 0.
Separatore di nuove linee indipendente dalla piattaforma
Poiché il nuovo separatore di linee varia da piattaforma a piattaforma (ad es. \n
su sistemi di tipo Unix o \r\n
su Windows) è spesso necessario avere un modo indipendente dalla piattaforma di accedervi. In Java può essere recuperato da una proprietà di sistema:
System.getProperty("line.separator")
Poiché il nuovo separatore di righe è così comunemente necessario, da Java 7 su un metodo di scelta rapida che restituisce esattamente lo stesso risultato del codice precedente è disponibile:
System.lineSeparator()
Nota : poiché è molto improbabile che il nuovo separatore di riga cambi durante l'esecuzione del programma, è consigliabile archiviarlo in una variabile finale statica anziché recuperarlo dalla proprietà di sistema ogni volta che è necessario.
Quando si utilizza String.format
, utilizzare %n
anziché \n
o '\ r \ n' per generare un nuovo separatore di riga indipendente dalla piattaforma.
System.out.println(String.format('line 1: %s.%nline 2: %s%n', lines[0],lines[1]));
Aggiunta del metodo toString () per oggetti personalizzati
Supponiamo di aver definito la seguente classe Person
:
public class Person {
String name;
int age;
public Person (int age, String name) {
this.age = age;
this.name = name;
}
}
Se istanziate un nuovo oggetto Person
:
Person person = new Person(25, "John");
e più avanti nel tuo codice usi la seguente dichiarazione per stampare l'oggetto:
System.out.println(person.toString());
otterrai un risultato simile al seguente:
Person@7ab89d
Questo è il risultato dell'implementazione del metodo toString()
definito nella classe Object
, una superclasse di Person
. La documentazione di Object.toString()
afferma:
Il metodo toString per l'oggetto classe restituisce una stringa composta dal nome della classe di cui l'oggetto è un'istanza, il carattere at-sign `@ 'e la rappresentazione esadecimale senza firma del codice hash dell'oggetto. In altre parole, questo metodo restituisce una stringa uguale al valore di:
getClass().getName() + '@' + Integer.toHexString(hashCode())
Quindi, per un output significativo, dovrai sovrascrivere il metodo toString()
:
@Override
public String toString() {
return "My name is " + this.name + " and my age is " + this.age;
}
Ora l'output sarà:
My name is John and my age is 25
Puoi anche scrivere
System.out.println(person);
Infatti, println()
richiama implicitamente il metodo toString
sull'oggetto.
Spaccare le corde
È possibile dividere una String
su un particolare carattere di delimitazione o un'espressione regolare , è possibile utilizzare il metodo String.split()
con la seguente firma:
public String[] split(String regex)
Si noti che la delimitazione del carattere o dell'espressione regolare viene rimossa dalla matrice di stringhe risultante.
Esempio utilizzando il carattere di delimitazione:
String lineFromCsvFile = "Mickey;Bolton;12345;121216";
String[] dataCells = lineFromCsvFile.split(";");
// Result is dataCells = { "Mickey", "Bolton", "12345", "121216"};
Esempio usando l'espressione regolare:
String lineFromInput = "What do you need from me?";
String[] words = lineFromInput.split("\\s+"); // one or more space chars
// Result is words = {"What", "do", "you", "need", "from", "me?"};
Puoi anche dividere direttamente un valore letterale String
:
String[] firstNames = "Mickey, Frank, Alicia, Tom".split(", ");
// Result is firstNames = {"Mickey", "Frank", "Alicia", "Tom"};
Avvertenza : non dimenticare che il parametro viene sempre considerato come un'espressione regolare.
"aaa.bbb".split("."); // This returns an empty array
Nell'esempio precedente .
viene considerato come il carattere jolly di espressione regolare che corrisponde a qualsiasi carattere e poiché ogni carattere è un delimitatore, il risultato è un array vuoto.
Divisione basata su un delimitatore che è un meta-carattere regex
I seguenti personaggi sono considerati speciali (detti anche meta-caratteri) in espressioni regolari
< > - = ! ( ) [ ] { } \ ^ $ | ? * + .
Per dividere una stringa in base a uno dei delimitatori sopra indicati, è necessario Pattern.quote()
l' escape usando \\
o utilizzare Pattern.quote()
:
Utilizzando
Pattern.quote()
:String s = "a|b|c"; String regex = Pattern.quote("|"); String[] arr = s.split(regex);
Sfuggire ai caratteri speciali:
String s = "a|b|c"; String[] arr = s.split("\\|");
Split rimuove i valori vuoti
split(delimiter)
per default rimuove le stringhe vuote finali dall'array dei risultati. Per disattivare questo meccanismo, è necessario utilizzare la versione di split(delimiter, limit)
con limite impostato su valore negativo come
String[] split = data.split("\\|", -1);
split(regex)
restituisce internamente il risultato di split(regex, 0)
.
Il parametro limit controlla il numero di volte in cui il pattern è applicato e quindi influenza la lunghezza dell'array risultante.
Se il limite n è maggiore di zero, il pattern verrà applicato al massimo n - 1 volte, la lunghezza dell'array non sarà maggiore di n e l'ultima voce dell'array conterrà tutti gli input oltre l'ultimo delimitatore corrispondente.
Se n è negativo, il pattern verrà applicato il maggior numero possibile di volte e l'array può avere una lunghezza qualsiasi.
Se n è zero, il pattern verrà applicato il maggior numero di volte possibile, l'array può avere una lunghezza qualsiasi e le stringhe vuote verranno eliminate.
Divisione con StringTokenizer
Oltre al metodo split()
, le stringhe possono anche essere suddivise usando StringTokenizer
.
StringTokenizer
è ancora più restrittivo di String.split()
, e anche un po 'più difficile da usare. È essenzialmente progettato per estrarre token delimitati da un set fisso di caratteri (dati come String
). Ogni personaggio fungerà da separatore. A causa di questa restrizione, è circa il doppio della velocità di String.split()
.
I set di caratteri predefiniti sono spazi vuoti ( \t\n\r\f
). L'esempio seguente stamperà ogni parola separatamente.
String str = "the lazy fox jumped over the brown fence";
StringTokenizer tokenizer = new StringTokenizer(str);
while (tokenizer.hasMoreTokens()) {
System.out.println(tokenizer.nextToken());
}
Questo verrà stampato:
the
lazy
fox
jumped
over
the
brown
fence
È possibile utilizzare diversi set di caratteri per la separazione.
String str = "jumped over";
// In this case character `u` and `e` will be used as delimiters
StringTokenizer tokenizer = new StringTokenizer(str, "ue");
while (tokenizer.hasMoreTokens()) {
System.out.println(tokenizer.nextToken());
}
Questo verrà stampato:
j
mp
d ov
r
Unione di stringhe con un delimitatore
Un array di stringhe può essere unito usando il metodo statico String.join()
:
String[] elements = { "foo", "bar", "foobar" };
String singleString = String.join(" + ", elements);
System.out.println(singleString); // Prints "foo + bar + foobar"
Allo stesso modo, c'è un metodo String.join()
sovraccarico per Iterable
s.
Per avere un controllo dettagliato sull'unione , è possibile utilizzare la classe StringJoiner :
StringJoiner sj = new StringJoiner(", ", "[", "]");
// The last two arguments are optional,
// they define prefix and suffix for the result string
sj.add("foo");
sj.add("bar");
sj.add("foobar");
System.out.println(sj); // Prints "[foo, bar, foobar]"
Per unire un flusso di stringhe, puoi utilizzare il raccoglitore di join:
Stream<String> stringStream = Stream.of("foo", "bar", "foobar");
String joined = stringStream.collect(Collectors.joining(", "));
System.out.println(joined); // Prints "foo, bar, foobar"
C'è anche un'opzione per definire prefisso e suffisso :
Stream<String> stringStream = Stream.of("foo", "bar", "foobar");
String joined = stringStream.collect(Collectors.joining(", ", "{", "}"));
System.out.println(joined); // Prints "{foo, bar, foobar}"
Inversione di archi
Ci sono un paio di modi in cui puoi invertire una stringa per farla tornare indietro.
StringBuilder / StringBuffer:
String code = "code"; System.out.println(code); StringBuilder sb = new StringBuilder(code); code = sb.reverse().toString(); System.out.println(code);
Array di caratteri:
String code = "code"; System.out.println(code); char[] array = code.toCharArray(); for (int index = 0, mirroredIndex = array.length - 1; index < mirroredIndex; index++, mirroredIndex--) { char temp = array[index]; array[index] = array[mirroredIndex]; array[mirroredIndex] = temp; } // print reversed System.out.println(new String(array));
Conteggio delle occorrenze di una sottostringa o di un carattere in una stringa
countMatches
metodo countMatches
di org.apache.commons.lang3.StringUtils viene in genere utilizzato per contare le occorrenze di una sottostringa o di un carattere in una String
:
import org.apache.commons.lang3.StringUtils;
String text = "One fish, two fish, red fish, blue fish";
// count occurrences of a substring
String stringTarget = "fish";
int stringOccurrences = StringUtils.countMatches(text, stringTarget); // 4
// count occurrences of a char
char charTarget = ',';
int charOccurrences = StringUtils.countMatches(text, charTarget); // 3
In caso contrario, per le stesse API Java standard è possibile utilizzare le espressioni regolari:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
String text = "One fish, two fish, red fish, blue fish";
System.out.println(countStringInString("fish", text)); // prints 4
System.out.println(countStringInString(",", text)); // prints 3
public static int countStringInString(String search, String text) {
Pattern pattern = Pattern.compile(search);
Matcher matcher = pattern.matcher(text);
int stringOccurrences = 0;
while (matcher.find()) {
stringOccurrences++;
}
return stringOccurrences;
}
Concatenazione di stringhe e StringBuilder
La concatenazione di stringhe può essere eseguita usando l'operatore +
. Per esempio:
String s1 = "a";
String s2 = "b";
String s3 = "c";
String s = s1 + s2 + s3; // abc
Normalmente un'implementazione del compilatore eseguirà la suddetta concatenazione usando metodi che coinvolgono un StringBuilder
sotto il cofano. Una volta compilato, il codice sarà simile al seguente:
StringBuilder sb = new StringBuilder("a");
String s = sb.append("b").append("c").toString();
StringBuilder
ha diversi metodi di overloading per aggiungere tipi diversi, ad esempio, per aggiungere un int
posto di una String
. Ad esempio, un'implementazione può convertire:
String s1 = "a";
String s2 = "b";
String s = s1 + s2 + 2; // ab2
al seguente:
StringBuilder sb = new StringBuilder("a");
String s = sb.append("b").append(2).toString();
Gli esempi sopra illustrano una semplice operazione di concatenazione che viene effettivamente eseguita in un singolo punto nel codice. La concatenazione implica una singola istanza di StringBuilder
. In alcuni casi, una concatenazione viene eseguita in modo cumulativo come in un ciclo:
String result = "";
for(int i = 0; i < array.length; i++) {
result += extractElement(array[i]);
}
return result;
In questi casi, l'ottimizzazione del compilatore di solito non viene applicata e ogni iterazione creerà un nuovo oggetto StringBuilder
. Questo può essere ottimizzato trasformando esplicitamente il codice per utilizzare un singolo StringBuilder
:
StringBuilder result = new StringBuilder();
for(int i = 0; i < array.length; i++) {
result.append(extractElement(array[i]));
}
return result.toString();
Un StringBuilder
verrà inizializzato con uno spazio vuoto di soli 16 caratteri. Se si sa in anticipo che si costruiscono stringhe più grandi, può essere utile inizializzarlo con dimensioni sufficienti in anticipo, in modo che il buffer interno non debba essere ridimensionato:
StringBuilder buf = new StringBuilder(30); // Default is 16 characters
buf.append("0123456789");
buf.append("0123456789"); // Would cause a reallocation of the internal buffer otherwise
String result = buf.toString(); // Produces a 20-chars copy of the string
Se stai producendo molte stringhe, è consigliabile riutilizzare StringBuilder
:
StringBuilder buf = new StringBuilder(100);
for (int i = 0; i < 100; i++) {
buf.setLength(0); // Empty buffer
buf.append("This is line ").append(i).append('\n');
outputfile.write(buf.toString());
}
Se (e solo se) più thread scrivono nello stesso buffer, utilizzare StringBuffer , che è una versione synchronized
di StringBuilder
. Ma poiché solitamente un solo thread scrive su un buffer, di solito è più veloce usare StringBuilder
senza sincronizzazione.
Usando il metodo concat ():
String string1 = "Hello ";
String string2 = "world";
String string3 = string1.concat(string2); // "Hello world"
Ciò restituisce una nuova stringa che è stringa1 con stringa2 aggiunta ad essa alla fine. Puoi anche usare il metodo concat () con stringhe letterali, come in:
"My name is ".concat("Buyya");
Sostituzione di parti di stringhe
Due modi per sostituire: regex o per corrispondenza esatta.
Nota: l'oggetto String originale sarà invariato, il valore restituito mantiene la stringa modificata.
Corrispondenza esatta
Sostituisci un singolo carattere con un altro singolo carattere:
String replace(char oldChar, char newChar)
Restituisce una nuova stringa risultante dalla sostituzione di tutte le occorrenze di oldChar in questa stringa con newChar.
String s = "popcorn";
System.out.println(s.replace('p','W'));
Risultato:
WoWcorn
Sostituisci la sequenza di caratteri con un'altra sequenza di caratteri:
String replace(CharSequence target, CharSequence replacement)
Sostituisce ogni sottostringa di questa stringa che corrisponde alla sequenza di destinazione letterale con la sequenza di sostituzione letterale specificata.
String s = "metal petal et al.";
System.out.println(s.replace("etal","etallica"));
Risultato:
metallica petallica et al.
regex
Nota : il raggruppamento usa il carattere $
per fare riferimento ai gruppi, ad esempio $1
.
Sostituisci tutte le partite:
String replaceAll(String regex, String replacement)
Sostituisce ogni sottostringa di questa stringa che corrisponde all'espressione regolare data con la sostituzione fornita.
String s = "spiral metal petal et al.";
System.out.println(s.replaceAll("(\\w*etal)","$1lica"));
Risultato:
spiral metallica petallica et al.
Sostituisci solo la prima partita:
String replaceFirst(String regex, String replacement)
Sostituisce la prima sottostringa di questa stringa che corrisponde all'espressione regolare data con la sostituzione specificata
String s = "spiral metal petal et al.";
System.out.println(s.replaceAll("(\\w*etal)","$1lica"));
Risultato:
spiral metallica petal et al.
Rimuovi spazi bianchi dall'inizio e alla fine di una stringa
Il metodo trim()
restituisce una nuova stringa con gli spazi bianchi iniziali e finali rimossi.
String s = new String(" Hello World!! ");
String t = s.trim(); // t = "Hello World!!"
Se si trim
una stringa che non ha spazi bianchi da rimuovere, verrà restituita la stessa istanza String.
Si noti che il metodo trim()
ha la propria nozione di spazio bianco , che differisce dalla nozione utilizzata dal metodo Character.isWhitespace()
:
Tutti i caratteri di controllo ASCII con i codici da
U+0000
aU+0020
sono considerati spazi bianchi e vengono rimossi datrim()
. Questo includeU+0020 'SPACE'
,U+0009 'CHARACTER TABULATION'
,U+000A 'LINE FEED'
eU+000D 'CARRIAGE RETURN'
, ma anche i caratteri comeU+0007 'BELL'
.Gli spazi bianchi Unicode come
U+00A0 'NO-BREAK SPACE'
oU+2003 'EM SPACE'
non sono riconosciuti datrim()
.
Pool di stringhe e archiviazione heap
Come molti oggetti Java, tutte le istanze String
vengono create sullo heap, anche letterali. Quando la JVM trova un valore letterale String
che non ha riferimenti equivalenti nell'heap, la JVM crea un'istanza String
corrispondente nell'heap e memorizza anche un riferimento all'istanza String
appena creata nel pool String. Qualsiasi altro riferimento allo stesso letterale String
viene sostituito con l'istanza String
precedentemente creata nell'heap.
Diamo un'occhiata al seguente esempio:
class Strings
{
public static void main (String[] args)
{
String a = "alpha";
String b = "alpha";
String c = new String("alpha");
//All three strings are equivalent
System.out.println(a.equals(b) && b.equals(c));
//Although only a and b reference the same heap object
System.out.println(a == b);
System.out.println(a != c);
System.out.println(b != c);
}
}
L'output di quanto sopra è:
true
true
true
true
Quando si usano le doppie virgolette per creare una stringa, prima cerca String con lo stesso valore nel pool String, se trovato restituisce solo il riferimento altrimenti crea una nuova stringa nel pool e quindi restituisce il riferimento.
Tuttavia, utilizzando un nuovo operatore, imponiamo la classe String per creare un nuovo oggetto String nello spazio heap. Possiamo usare il metodo intern () per metterlo nel pool o fare riferimento ad un altro oggetto String dal pool di stringhe con lo stesso valore.
Anche il pool di stringhe stesso viene creato nell'heap.
Prima di Java 7, i valori letterali String
venivano memorizzati nel pool costante di runtime nell'area del metodo di PermGen
, che aveva una dimensione fissa.
Il pool di stringhe risiedeva anche in PermGen
.
In JDK 7, le stringhe internate non sono più allocate nella generazione permanente dell'heap Java, ma sono allocate nella parte principale dell'heap Java (noto come generazioni giovani e meno recenti), insieme agli altri oggetti creati dall'applicazione . Questo cambiamento comporterà più dati residenti nell'heap principale di Java e meno dati nella generazione permanente e, pertanto, potrebbe richiedere la regolazione delle dimensioni dell'heap. La maggior parte delle applicazioni vedrà solo differenze relativamente piccole nell'utilizzo dell'heap a causa di questa modifica, ma applicazioni più grandi che caricano molte classi o fanno un uso massiccio del metodo
String.intern()
vedranno differenze più significative.
Interruttore non sensibile al maiuscolo / minuscolo
switch
stesso non può essere parametrizzato senza distinzione tra maiuscole e minuscole, ma se strettamente necessario, può comportarsi in modo insensibile alla stringa di input utilizzando toLowerCase()
o toUpperCase
:
switch (myString.toLowerCase()) {
case "case1" :
...
break;
case "case2" :
...
break;
}
diffidare
-
Locale
potrebbero influenzare il modo in cui i casi cambiano ! - Bisogna fare attenzione a non avere caratteri maiuscoli nelle etichette - quelli non verranno mai eseguiti!