Was ist der einfachste / beste / korrekteste Weg, um die Zeichen eines Strings in Java zu durchlaufen?

StringTokenizer ? Konvertiere den String in ein char[] und iteriere darüber? Etwas anderes?

Solutions Collecting From Web of "Was ist der einfachste / beste / korrekteste Weg, um die Zeichen eines Strings in Java zu durchlaufen?"

Ich benutze eine for-Schleife, um die Zeichenfolge zu iterieren und charAt() zu verwenden, um jedes Zeichen zu untersuchen. Da die charAt() mit einem Array implementiert wird, ist die charAt() Methode eine konstante charAt() .

 String s = "...stuff..."; for (int i = 0; i < s.length(); i++){ char c = s.charAt(i); //Process char } 

Das würde ich tun. Es scheint mir am einfachsten zu sein.

Was die Korrektheit betrifft, glaube ich nicht, dass das hier existiert. Es basiert alles auf Ihrem persönlichen Stil.

Zwei Optionen

 for(int i = 0, n = s.length() ; i < n ; i++) { char c = s.charAt(i); } 

oder

 for(char c : s.toCharArray()) { // process c } 

Der erste ist wahrscheinlich schneller, dann ist der 2. wahrscheinlich lesbarer.

Beachten Sie, dass die meisten anderen hier beschriebenen Techniken nicht funktionieren, wenn Sie mit Zeichen außerhalb der BMP (Unicode Basic Multilingual Plane ) arbeiten, also Codepunkten , die außerhalb des u0000-uFFFF-Bereichs liegen. Dies wird nur selten der Fall sein, da die Codepunkte außerhalb dieser meist toten Sprachen zugeordnet sind. Aber es gibt einige nützliche Zeichen außerhalb, zum Beispiel einige Codepunkte, die für die mathematische Notation verwendet werden, und einige, die verwendet werden, um Eigennamen auf Chinesisch zu codieren.

In diesem Fall lautet Ihr Code:

 String str = "...."; int offset = 0, strLen = str.length(); while (offset < strLen) { int curChar = str.codePointAt(offset); offset += Character.charCount(curChar); // do something with curChar } 

Die Character.charCount(int) -Methode erfordert Java 5+.

Quelle: http://mindprod.com/jgloss/codepoint.html

Ich stimme zu, dass StringTokenizer hier zu viel ist. Eigentlich habe ich die obigen Vorschläge ausprobiert und mir die Zeit genommen.

Mein Test war ziemlich einfach: Erstelle einen StringBuilder mit ungefähr einer Million Zeichen, konvertiere ihn in einen String und traversiere jeden von ihnen mit charAt () / nach der Konvertierung in ein char-Array / mit einem CharacterIterator tausendmal (natürlich sicher Tun Sie etwas in der Zeichenfolge, damit der Compiler die ganze Schleife nicht optimieren kann :-)).

Das Ergebnis auf meinem 2,6 GHz Powerbook (das ist ein Mac :-)) und JDK 1.5:

  • Test 1: charAt + String -> 3138 ms
  • Test 2: String konvertiert in Array -> 9568msec
  • Test 3: StringBuilder charAt -> 3536 ms
  • Test 4: CharacterIterator und String -> 12151 ms

Da die Ergebnisse signifikant unterschiedlich sind, scheint auch der einfachste Weg der schnellste zu sein. Interessanterweise scheint charAt () eines StringBuilders etwas langsamer zu sein als der von String.

Übrigens schlage ich vor, CharacterIterator nicht zu verwenden, da ich den Missbrauch des “\ uFFFF” -Zeichens als “Ende der Iteration” für einen wirklich schrecklichen Hack halte. In großen Projekten gibt es immer zwei Typen, die den gleichen Hack für zwei verschiedene Zwecke verwenden und der Code stürzt auf mysteriöse Weise ab.

Hier ist einer der Tests:

  int count = 1000; ... System.out.println("Test 1: charAt + String"); long t = System.currentTimeMillis(); int sum=0; for (int i=0; i 

Dafür gibt es einige dedizierte classn:

 import java.text.*; final CharacterIterator it = new StringCharacterIterator(s); for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) { // process c ... } 

Wenn Sie Guava auf Ihrem classnpfad haben, ist das folgende eine ziemlich lesbare Alternative. Guava hat sogar eine ziemlich vernünftige benutzerdefinierte Listen-Implementierung für diesen Fall, daher sollte dies nicht ineffizient sein.

 for(char c : Lists.charactersOf(yourString)) { // Do whatever you want } 

UPDATE: Wie @Alex bemerkt hat, gibt es bei Java 8 auch CharSequence#chars . Sogar der Typ ist IntStream, also kann er wie folgt chars zugeordnet werden:

 yourString.chars() .mapToObj(c -> Character.valueOf((char) c)) .forEach(c -> System.out.println(c)); // Or whatever you want 

Wenn Sie die Codepunkte eines Strings durchlaufen müssen (siehe diese Antwort ), können CharSequence#codePoints Methode CharSequence#codePoints , die in Java 8 hinzugefügt wurde, CharSequence#codePoints verwenden:

 for(int c : string.codePoints().toArray()){ ... } 

oder den Stream direkt anstelle einer for-Schleife verwenden:

 string.codePoints().forEach(c -> ...); 

Es gibt auch CharSequence#chars wenn Sie einen Stream der Zeichen wünschen (obwohl es ein IntStream , da es keinen CharStream ).

In Java 8 können wir es wie folgt lösen:

 String str = "xyz"; str.chars().forEachOrdered(i -> System.out.print((char)i)); str.codePoints().forEachOrdered(i -> System.out.print((char)i)); 

Die Methode chars () gibt einen IntStream wie in doc beschrieben zurück :

Gibt einen Stream von int zurück, der die char-Werte aus dieser Sequenz auf null setzt. Ein beliebiges Zeichen, das einem Ersatzcodepunkt zugeordnet ist, wird uninterpretiert übergeben. Wenn die Sequenz mutiert wird, während der Stream gelesen wird, ist das Ergebnis nicht definiert.

Die Methode codePoints() auch einen IntStream gemäß Dokument zurück:

Gibt einen Strom von Codepunktwerten aus dieser Sequenz zurück. Alle in der Sequenz gefundenen Ersatzpaare werden wie bei Character.toCodePoint kombiniert und das Ergebnis wird an den Stream übergeben. Alle anderen Code-Einheiten, einschließlich normaler BMP-Zeichen, ungepaarter Surrogate und undefinierter Code-Einheiten, werden auf int-Werte auf Null gesetzt, die dann an den Stream übergeben werden.

Wie unterscheiden sich Char und Code? Wie in diesem Artikel erwähnt:

Unicode 3.1 fügte zusätzliche Zeichen hinzu, wodurch die Gesamtzahl der Zeichen auf mehr als die 216 Zeichen erhöht wird, die durch ein einzelnes 16-Bit-Zeichen unterschieden werden können. Daher hat ein char Wert keine Eins-zu-Eins-Zuordnung zur fundamentalen semantischen Einheit in Unicode. JDK 5 wurde aktualisiert, um den größeren Satz von Zeichenwerten zu unterstützen. Anstatt die Definition des char Typs zu ändern, werden einige der neuen zusätzlichen Zeichen durch ein Ersatzpaar aus zwei char Werten dargestellt. Um Benennungsverwechslungen zu vermeiden, wird ein Codepunkt verwendet, um auf die Nummer zu verweisen, die ein bestimmtes Unicode-Zeichen darstellt, einschließlich ergänzender Zeichen.

Schließlich warum forEachOrdered und nicht forEach ?

Das Verhalten von forEach ist explizit nichtdeterministisch, wofür forEachOrdered eine Aktion für jedes Element dieses Streams in der Aufeinanderfolge des forEachOrdered ausführt, wenn der Stream eine definierte Begegnungsreihenfolge hat. forEach garantiert also nicht, dass die Bestellung aufbewahrt wird. Überprüfen Sie diese Frage für weitere Informationen.

Für den Unterschied zwischen einem Zeichen, einem Codepunkt, einer Glyphe und einem Graphem überprüfen Sie diese Frage .

Ich würde StringTokenizer nicht verwenden, da es sich um eine class im JDK handelt, die StringTokenizer ist.

Das Javadoc sagt:

StringTokenizer ist eine Legacy-class, die aus Kompatibilitätsgründen beibehalten wird, obwohl deren Verwendung in neuem Code nicht StringTokenizer wird. Es wird empfohlen, dass jeder, der diese functionalität sucht, stattdessen die Split-Methode von String oder das Paket java.util.regex verwendet.

Siehe Java-Tutorials: Strings .

 public class StringDemo { public static void main(String[] args) { String palindrome = "Dot saw I was Tod"; int len = palindrome.length(); char[] tempCharArray = new char[len]; char[] charArray = new char[len]; // put original string in an array of chars for (int i = 0; i < len; i++) { tempCharArray[i] = palindrome.charAt(i); } // reverse array of chars for (int j = 0; j < len; j++) { charArray[j] = tempCharArray[len - 1 - j]; } String reversePalindrome = new String(charArray); System.out.println(reversePalindrome); } } 

Setzen Sie die Länge in int len und verwenden Sie for Schleife.

StringTokenizer ist völlig ungeeignet für die Aufgabe, einen String in seine individuellen Zeichen zu zerlegen. Mit String#split() können Sie das einfach tun, indem Sie eine Regex verwenden, die nichts enthält, zB:

 String[] theChars = str.split("|"); 

Aber StringTokenizer verwendet keine Regexes, und es gibt keine Trennzeichenfolge, die Sie angeben können, die mit dem Nichts zwischen Zeichen übereinstimmt. Es gibt einen netten kleinen Hack, den Sie verwenden können, um das Gleiche zu erreichen: Verwenden Sie die Zeichenfolge selbst als Begrenzungszeichenfolge (machen Sie jedes Zeichen darin zu einem Begrenzer) und lassen Sie sie die Begrenzer zurückgeben:

 StringTokenizer st = new StringTokenizer(str, str, true); 

Ich erwähne diese Optionen jedoch nur, um sie zu vercasting. Bei beiden Techniken wird die ursprüngliche Zeichenfolge in Zeichenfolgen mit einem Zeichen anstelle von Zeichenelementarimalen zerlegt, und beide enthalten einen hohen Aufwand in Form der Objektgenerierung und der Zeichenfolgenmanipulation. Vergleichen Sie das mit dem Aufruf von charAt () in einer for-Schleife, die praktisch keinen Overhead verursacht.

Ausarbeitung dieser Antwort und dieser Antwort .

Die obigen Antworten weisen auf das Problem vieler der hier aufgeführten Lösungen hin, die nicht nach Codepunktwerten iterieren – sie hätten Probleme mit Ersatzzeichen . Die Java-Dokumente beschreiben das Problem auch hier (siehe “Unicode-Zeichenrepräsentationen”). Wie auch immer, hier ist ein Code, der einige Ersatzzeichen aus dem zusätzlichen Unicode-Set verwendet und diese zurück in einen String konvertiert. Beachten Sie, dass .toChars () ein Array von Zeichen zurückgibt: Wenn Sie mit Surrogaten zu tun haben, benötigen Sie zwei Zeichen. Dieser Code sollte für jedes Unicode-Zeichen funktionieren.

  String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓"; supplementary.codePoints().forEach(cp -> System.out.print(new String(Character.toChars(cp)))); 

Dieser Beispielcode wird Ihnen helfen!

 import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; public class Solution { public static void main(String[] args) { HashMap map = new HashMap(); map.put("a", 10); map.put("b", 30); map.put("c", 50); map.put("d", 40); map.put("e", 20); System.out.println(map); Map sortedMap = sortByValue(map); System.out.println(sortedMap); } public static Map sortByValue(Map unsortedMap) { Map sortedMap = new TreeMap(new ValueComparator(unsortedMap)); sortedMap.putAll(unsortedMap); return sortedMap; } } class ValueComparator implements Comparator { Map map; public ValueComparator(Map map) { this.map = map; } public int compare(Object keyA, Object keyB) { Comparable valueA = (Comparable) map.get(keyA); Comparable valueB = (Comparable) map.get(keyB); return valueB.compareTo(valueA); } }