StringTokenizer
? Konvertieren Sie das String
in ein char[]
und iterieren Sie darüber? Etwas anderes?
StringTokenizer
? Konvertieren Sie das String
in ein char[]
und iterieren Sie darüber? Etwas anderes?
Antworten:
Ich benutze eine for-Schleife, um die Zeichenfolge zu iterieren und charAt()
jedes Zeichen dazu zu bringen, sie zu untersuchen. Da der String mit einem Array implementiert ist, ist die charAt()
Methode eine Operation mit konstanter Zeit.
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 es das hier gibt. Es basiert alles auf Ihrem persönlichen Stil.
String.charAt(int)
tut nur value[index]
. Ich denke, Sie verwechseln chatAt()
etwas anderes, das Ihnen Codepunkte gibt.
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, der zweite ist wahrscheinlich besser lesbar.
Beachten Sie, dass die meisten anderen hier beschriebenen Techniken nicht funktionieren , wenn Sie Zeichen außerhalb des BMP (Unicode Basic Multilingual Plane ) verwenden, dh Codepunkte , die außerhalb des Bereichs u0000-uFFFF liegen. Dies kommt nur selten vor, da die Codepunkte außerhalb meist toten Sprachen zugeordnet sind. Es gibt jedoch einige nützliche Zeichen außerhalb davon, zum Beispiel einige Codepunkte, die für die mathematische Notation verwendet werden, und einige, die zum Codieren von Eigennamen auf Chinesisch verwendet werden.
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+.
Ich bin damit einverstanden, dass StringTokenizer hier übertrieben ist. Eigentlich habe ich die obigen Vorschläge ausprobiert und mir die Zeit genommen.
Mein Test war ziemlich einfach: Erstellen Sie einen StringBuilder mit ungefähr einer Million Zeichen, konvertieren Sie ihn in einen String und durchlaufen Sie jeden von ihnen mit charAt () / nach tausendmaliger Konvertierung in ein char-Array / mit einem CharacterIterator (natürlich stellen Sie dies sicher) Mach etwas an der Zeichenfolge, damit der Compiler nicht die gesamte Schleife optimieren kann :-)).
Das Ergebnis auf meinem 2,6 GHz Powerbook (das ist ein Mac :-)) und JDK 1.5:
Da die Ergebnisse erheblich voneinander abweichen, scheint der einfachste Weg auch der schnellste zu sein. Interessanterweise scheint charAt () eines StringBuilder etwas langsamer zu sein als das von String.
Übrigens schlage ich vor, CharacterIterator nicht zu verwenden, da ich den Missbrauch des Zeichens '\ uFFFF' als "Ende der Iteration" für einen wirklich schrecklichen Hack halte. In großen Projekten gibt es immer zwei Leute, die dieselbe Art von Hack für zwei verschiedene Zwecke verwenden, und der Code stürzt wirklich mysteriös 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<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");
In Java 8 können wir es lösen als:
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 ein IntStream
wie in doc erwähnt zurück :
Gibt einen Stream von int Null zurück, der die Zeichenwerte aus dieser Sequenz erweitert. Jedes Zeichen, das einem Ersatzcodepunkt zugeordnet ist, wird nicht interpretiert durchlaufen. Wenn die Sequenz während des Lesens des Streams mutiert ist, ist das Ergebnis undefiniert.
Die Methode gibt codePoints()
außerdem ein IntStream
Dokument gemäß Dokument zurück:
Gibt einen Strom von Codepunktwerten aus dieser Sequenz zurück. Alle in der Sequenz angetroffenen Ersatzpaare werden wie von Character.toCodePoint kombiniert und das Ergebnis an den Stream übergeben. Alle anderen Codeeinheiten, einschließlich gewöhnlicher BMP-Zeichen, ungepaarter Ersatzzeichen und undefinierter Codeeinheiten, werden auf int-Werte erweitert, die dann an den Stream übergeben werden.
Wie unterscheiden sich char und code point? Wie in diesem Artikel erwähnt:
Unicode 3.1 fügte zusätzliche Zeichen hinzu und erhöhte die Gesamtzahl der Zeichen auf mehr als die 216 Zeichen, die durch ein einzelnes 16-Bit unterschieden werden können
char
. Daher ist einchar
Wert nicht mehr eins zu eins der grundlegenden semantischen Einheit in Unicode zugeordnet. JDK 5 wurde aktualisiert, um den größeren Satz von Zeichenwerten zu unterstützen. Anstatt die Definition deschar
Typs zu ändern , werden einige der neuen Zusatzzeichen durch ein Ersatzpaar aus zweichar
Werten dargestellt. Um die Verwirrung bei der Benennung zu verringern, wird ein Codepunkt verwendet, um auf die Nummer zu verweisen, die ein bestimmtes Unicode-Zeichen darstellt, einschließlich zusätzlicher Zeichen.
Endlich warum forEachOrdered
und nicht forEach
?
Das Verhalten von forEach
ist explizit nicht deterministisch, wenn der forEachOrdered
Benutzer eine Aktion für jedes Element dieses Streams in der Begegnungsreihenfolge des Streams ausführt, wenn der Stream eine definierte Begegnungsreihenfolge hat. So forEach
garantiert nicht , dass die Reihenfolge gehalten werden würde. Überprüfen Sie auch diese Frage für mehr.
Für Unterschied zwischen einem Charakter, ein Codepunkt, eine Glyphe und einem Graphem dieser überprüfen Frage .
Hierfür gibt es einige spezielle Klassen:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
bereitstellt. Ein Java char
enthält 16 Bit und kann Unicode-Zeichen bis U + FFFF enthalten, Unicode gibt jedoch Zeichen bis U + 10FFFF an. Die Verwendung von 16 Bit zum Codieren von Unicode führt zu einer Zeichencodierung mit variabler Länge. Die meisten Antworten auf dieser Seite gehen davon aus, dass die Java-Codierung eine Codierung mit konstanter Länge ist, was falsch ist.
Wenn Sie Guava in Ihrem Klassenpfad haben, ist das Folgende eine ziemlich lesbare Alternative. Guava hat sogar eine ziemlich vernünftige benutzerdefinierte Listenimplementierung für diesen Fall, daher sollte dies nicht ineffizient sein.
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
UPDATE: Wie @Alex bemerkte, gibt es mit Java 8 auch CharSequence#chars
zu verwenden. Sogar der Typ ist IntStream, daher kann er Zeichen wie den folgenden zugeordnet werden:
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
Wenn Sie die Codepunkte von a String
(siehe diese Antwort ) durchlaufen müssen, können Sie die CharSequence#codePoints
in Java 8 hinzugefügte Methode kürzer / besser lesen :
for(int c : string.codePoints().toArray()){
...
}
oder verwenden Sie den Stream direkt anstelle einer for-Schleife:
string.codePoints().forEach(c -> ...);
Es gibt auch, CharSequence#chars
wenn Sie einen Stream der Zeichen wollen (obwohl es ein ist IntStream
, da es keine gibt CharStream
).
Ich würde es nicht verwenden, StringTokenizer
da es sich um eine der Klassen im JDK handelt, die Legacy sind.
Der Javadoc sagt:
StringTokenizer
ist eine Legacy-Klasse, die aus Kompatibilitätsgründen beibehalten wird, obwohl von ihrer Verwendung in neuem Code abgeraten wird. Es wird empfohlen, dass jeder, der diese Funktionalität sucht, die Split-Methode von verwendetString
java.util.regex
, stattdessen oder das Paket verwendet.
Wenn Sie Leistung benötigen, müssen Sie diese in Ihrer Umgebung testen . Kein anderer Weg.
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);
Auf Java online bekomme ich:
1 10349420
2 526130
3 484200
0
Auf Android x86 API 17 bekomme 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);
}
}
Geben Sie die Länge ein int len
und verwenden Sie die for
Schleife.
StringTokenizer ist völlig ungeeignet für die Aufgabe, einen String in seine einzelnen Zeichen zu zerlegen. Mit können String#split()
Sie dies einfach tun, indem Sie einen regulären Ausdruck verwenden, der mit nichts übereinstimmt, z.
String[] theChars = str.split("|");
StringTokenizer verwendet jedoch keine regulären Ausdrücke, und Sie können keine Trennzeichenfolge angeben, die mit dem Nichts zwischen den Zeichen übereinstimmt. Es gibt einen niedlichen kleinen Hack, mit dem Sie dasselbe erreichen können: Verwenden Sie die Zeichenfolge selbst als Trennzeichen (wodurch jedes Zeichen darin ein Trennzeichen wird) und lassen Sie die Trennzeichen zurückgeben:
StringTokenizer st = new StringTokenizer(str, str, true);
Ich erwähne diese Optionen jedoch nur, um sie abzulehnen. Beide Techniken unterteilen die ursprüngliche Zeichenfolge in Zeichenfolgen mit einem Zeichen anstelle von Zeichenprimitiven, und beide erfordern einen hohen Overhead in Form der Objekterstellung und der Zeichenfolgenmanipulation. Vergleichen Sie dies 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 . In den Java-Dokumenten wird das Problem auch hier beschrieben (siehe "Unicode-Zeichendarstellungen"). Wie auch immer, hier ist ein Code, der einige tatsächliche Ersatzzeichen aus dem zusätzlichen Unicode-Satz verwendet und diese wieder in einen String konvertiert . Beachten Sie, dass .toChars () ein Array von Zeichen zurückgibt: Wenn Sie mit Ersatzzeichen arbeiten, haben Sie notwendigerweise 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 hilft Ihnen dabei!
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);
}
}
Daher gibt es normalerweise zwei Möglichkeiten, um in Java durch Zeichenfolgen zu iterieren, die hier in diesem Thread bereits von mehreren Personen beantwortet wurden. Fügen Sie einfach meine Version hinzu, die First verwendet
String s = sc.next() // assuming scanner class is defined above
for(int i=0; i<s.length; i++){
s.charAt(i) // This being the first way and is a constant time operation will hardly add any overhead
}
char[] str = new char[10];
str = s.toCharArray() // this is another way of doing so and it takes O(n) amount of time for copying contents from your string class to character array
Wenn die Leistung auf dem Spiel steht, empfehle ich, die erste in konstanter Zeit zu verwenden. Wenn dies nicht der Fall ist, erleichtert die zweite Ihre Arbeit angesichts der Unveränderlichkeit mit String-Klassen in Java.