Was ist der einfachste / besten / richtige Weg durch die Zeichen einer Zeichenfolge in Java zu wiederholen?

StackOverflow https://stackoverflow.com/questions/196830

Frage

StringTokenizer? Konvertieren Sie die String zu einem char[] und iterieren das? Etwas anderes?

War es hilfreich?

Lösung

Ich verwende eine for-Schleife den String iterieren und verwenden charAt() jedes Zeichen, um es zu untersuchen. Da der String mit einem Array implementiert wird, ist die Methode charAt() ein konstanter Zeitbetrieb.

String s = "...stuff...";

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

Das ist, was ich tun würde. Es scheint, die am einfachsten zu mir.

Soweit Korrektheit geht, ich glaube nicht, dass existiert hier. Es ist ganz auf Ihren persönlichen Stil.

Andere Tipps

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
}

Die erste ist wahrscheinlich schneller, dann zweite ist wahrscheinlich besser lesbar.

Beachten Sie die meisten anderen hier beschriebenen Techniken zusammenbrechen, wenn Sie mit den Charakteren zu tun hat außerhalb der BMP (Unicode Basic Multilingual Plane ), dh Codepunkte , die außerhalb des u0000 sind -uFFFF Bereich. Dies wird nur selten vorkommen, da die Codepunkte außerhalb dieser meist zu toten Sprachen zugeordnet sind. Aber es gibt einige nützliche Zeichen außerhalb dieser, zum Beispiel einige Codepunkte für mathematische Schreibweise verwendet, und einige verwendet, um Eigennamen in chinesischen zu kodieren.

In diesem Fall Ihr Code sein:

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 bin damit einverstanden, dass StringTokenizer Overkill hier. Eigentlich habe ich versucht, über die Vorschläge aus und nahm sich die Zeit.

war mein Test recht einfach: einen String mit etwa einer Million Zeichen erzeugt, wandelt es in einen String, und durchquert jeweils mit charAt () / nach zu einem char-Array konvertieren / mit einem CharacterIterator tausendmal (natürlich dafür, dass etwas auf der Saite zu tun, damit der Compiler die ganze Schleife :-) nicht optimiert entfernt werden).

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

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

Wie die Ergebnisse signifikant verschieden sind, die einfachste Art und Weise scheint auch die schnellste zu sein. Interessanterweise charAt () ein String etwas langsamer zu sein scheint als die von String.

BTW schlage ich nicht CharacterIterator zu verwenden, wie ich seinen Missbrauch des ‚\ uFFFF‘ Charakter als „Ende der Iteration“ eine wirklich schreckliche Hack betrachten. In großen Projekten gibt es immer zwei Typen, die die gleiche Art von Hack für zwei unterschiedliche Zwecke und der Code stürzt wirklich auf mysteriöse Weise verwenden.

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<count; i++) {
        int len = str.length();
        for (int j=0; j<len; j++) {
            if (str.charAt(j) == 'b')
                sum = sum + 1;
        }
    }
    t = System.currentTimeMillis()-t;
    System.out.println("result: "+ sum + " after " + t + "msec");

Es gibt einige spezielle Klassen für diese:

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 Classpath, ist die folgende eine ziemlich lesbare Alternative . Guava hat sogar eine ziemlich vernünftige benutzerdefinierte Liste Implementierung für diesen Fall so sollte dies nicht ineffizient sein.

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

UPDATE: Wie @ Alex erwähnt, mit Java 8 gibt es auch CharSequence#chars zu verwenden. Auch der Typ IntStream, so kann es zu Zeichen abgebildet werden wie:

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

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

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

Die Methode Zeichen () gibt ein IntStream wie in doc :

  

Gibt einen Strom von int Null-Erweitern der char Werte von diesem   Reihenfolge. Alle Zeichen, die Karten zu einem Surrogat-Codepunkt geführt werden   durch uninterpretiert. Wenn die Sequenz mutiert ist, während der Stream   gelesen wird, ist das Ergebnis nicht definiert ist.

Die Methode codePoints() gibt auch eine IntStream gemäß doc:

  

Gibt einen Strom von Codepunktwerten aus dieser Sequenz. Irgendein   Ersatzpaar in der Sequenz angetroffen werden kombiniert, wie von   Character.toCodePoint und das Ergebnis wird in den Stream übergeben. Irgendein   andere Codeeinheiten, einschließlich gewöhnlichen BMP Zeichen, ungepaarten   Surrogate und undefinierte Codeeinheiten sind Null erweitert Werte in int   die dann in den Stream übergeben.

Wie ist char und Codepunkt anders? Wie bereits erwähnt in diese Artikel:

  

Unicode 3.1 hinzugefügt zusätzliche Zeichen, die Gesamtzahl bringen   von Zeichen auf mehr als die 216 Zeichen, die sein kann,   durch einen einzigen 16-Bit-char unterschieden. Daher ist ein char Wert nein   mehr hat eine Eins-zu-Eins-Zuordnung zu der Grundeinheit in semantischen   Unicode. JDK 5 wurde aktualisiert, um die größere Menge von Zeichen zu unterstützen   Werte. Anstatt die Definition der char Art zu ändern, einig   die neuen zusätzlichen Zeichen werden durch ein Ersatzpaar repräsentiert   von zwei char Werte. Zur Reduzierung der Benennung Verwirrung, wird ein Codepunkt sein   verwendet, um die Zahl zu entnehmen, die eine bestimmte Unicode darstellt   Charakter, einschließlich dem ergänzend denjenigen.

Schließlich warum forEachOrdered und nicht forEach?

Das Verhalten von forEach ist ausdrücklich nicht deterministisch, wo als die forEachOrdered für jedes Element dieses Stroms eine Aktion ausführt, in der Begegnung Reihenfolge des Stroms , wenn der Strom eine definierte Begegnung Ordnung hat. So forEach garantiert nicht, dass die Reihenfolge gehalten werden würde. Überprüfen Sie auch diese Frage für mehr.

Unterschied zwischen einem Charakter, ein Codepunkt, eine Glyphe und einem Graphem check this Frage .

Wenn Sie durch die Codepunkte eines String iterieren müssen (siehe diese Antwort ) eine kürzere / lesbare Weise ist die CharSequence#codePoints Verfahren hinzugefügt in Java 8:

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

oder den Strom direkt anstelle einer Verwendung für die Schleife:

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

Es gibt auch CharSequence#chars , wenn Sie einen Stream der Charaktere wollen (obwohl es ein IntStream ist, da es keine CharStream ist).

Ich würde nicht verwenden StringTokenizer, da es eines der Klassen in der JDK ist die Legacyinhalte.

Die javadoc sagt:

  

StringTokenizer ist ein Vermächtnis-Klasse,   wird aus Kompatibilitätsgründen beibehalten   obwohl seine Verwendung in neuen entmutigt   Code. Es wird empfohlen, dass jeder   sucht diese Funktionalität nutzen die   Spaltverfahren von String oder   java.util.regex Paket statt.

Wenn Sie die Leistung benötigen, dann Sie testen müssen auf Ihre Umgebung. Keine andere Art und Weise.

Hier Beispielcode:

int tmp = 0;
String s = new String(new byte[64*1024]);
{
    long st = System.nanoTime();
    for(int i = 0, n = s.length(); i < n; i++) {
        tmp += s.charAt(i);
    }
    st = System.nanoTime() - st;
    System.out.println("1 " + st);
}

{
    long st = System.nanoTime();
    char[] ch = s.toCharArray();
    for(int i = 0, n = ch.length; i < n; i++) {
        tmp += ch[i];
    }
    st = System.nanoTime() - st;
    System.out.println("2 " + st);
}
{
    long st = System.nanoTime();
    for(char c : s.toCharArray()) {
        tmp += c;
    }
    st = System.nanoTime() - st;
    System.out.println("3 " + st);
}
System.out.println("" + tmp);

Java Online ich:

1 10349420
2 526130
3 484200
0

Auf Android-x86-API 17 erhalte ich:

1 9122107
2 13486911
3 12700778
0

Siehe Die 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 for Schleife verwenden.

StringTokenizer ist völlig ungeeignet für die Aufgabe eine Zeichenfolge in seine einzelnen Zeichen zu brechen. Mit String#split() können Sie das leicht tun, indem Sie einen regulären Ausdruck verwenden, die nichts übereinstimmt, z.

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

Aber StringTokenizer nicht verwendet reguläre Ausdrücke, und es gibt keine Trennzeichenfolge Sie, dass angeben können die nichts zwischen den Zeichen übereinstimmen. Es ist ein nette kleines Hack Sie die gleiche Sache zu tun verwenden können: Verwenden Sie die Zeichenfolge selbst als Trennzeichen-String (was jedes Zeichen in ihm ein Trennzeichen) und hat es die Trennzeichen zurück:

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

Allerdings habe ich erwähnen, diese Optionen nur für den Zweck, sie zu entlassen. Beide Techniken brechen die ursprüngliche Zeichenfolge in ein Zeichenketten anstelle von char Primitiven, und beide beinhalten sehr viel Aufwand in Form von Objekterstellung und String-Manipulation. Vergleichen Sie das mit Aufruf charAt () in einer for-Schleife, die praktisch kein Aufwand entstehen.

diese Antwort und Surrogat Zeichen . Die Java-docs umreißen auch die Frage hier (siehe "Unicode Zeichendarstellungen"). Wie auch immer, hier ist ein Code, der einige tatsächlichen Surrogat Zeichen aus dem ergänzenden Unicode Satz verwendet, und wandelt sie zurück in einen String. Beachten Sie, dass .toChars () ein Array von Zeichen zurück: Wenn Sie mit Surrogaten zu tun haben, Sie müssen zwei Zeichen haben werden. Dieser Code sollte für Arbeit jeder Unicode-Zeichen.

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

Dieses Beispiel-Code wird Ihnen helfen, aus!

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<String, Integer> map = new HashMap<String, Integer>();
        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);
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top