StringTokenizer
?をに変換String
し、char[]
それを繰り返しますか?他に何か?
StringTokenizer
?をに変換String
し、char[]
それを繰り返しますか?他に何か?
回答:
forループを使用charAt()
して文字列を反復し、各文字を調べてそれを調べます。Stringは配列で実装されているため、このcharAt()
メソッドは一定時間の操作です。
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
それが私がすることです。それは私にとって最も簡単なようです。
正確さに関しては、それがここにあるとは思えません。それはすべてあなたの個人的なスタイルに基づいています。
String.charAt(int)
は単にやっているだけvalue[index]
です。私はあなたにchatAt()
コードポイントを与える何か他のものと混同していると思います。
2つのオプション
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
または
for(char c : s.toCharArray()) {
// process c
}
最初のものはおそらくより速く、2番目のものはおそらくより読みやすくなります。
BMP(Unicode Basic Multilingual Plane)の外の文字、つまりu0000-uFFFFの範囲外のコードポイントを処理している場合、ここで説明する他のテクニックのほとんどが機能しないことに注意してください。これ以外のコードポイントはほとんど死んだ言語に割り当てられているため、これはまれにしか起こりません。しかし、これ以外にもいくつかの便利な文字があります。たとえば、数学的表記に使用されるコードポイントや、中国語で適切な名前をエンコードするために使用されるコードポイントなどがあります。
その場合、コードは次のようになります。
String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
int curChar = str.codePointAt(offset);
offset += Character.charCount(curChar);
// do something with curChar
}
Character.charCount(int)
この方法は、Java 5+を必要とします。
StringTokenizerはここではやり過ぎだということに同意します。実際、私は上記の提案を試し、時間をかけました。
私のテストはかなり単純でした:約100万文字のStringBuilderを作成し、それをStringに変換し、charAt()でそれらをトラバースします/ char配列に変換した後/ CharacterIteratorで1000回(もちろん、コンパイラーがループ全体を最適化しないように文字列に何かを行います:-))。
私の2.6 GHz Powerbook(Macです:-))とJDK 1.5の結果:
結果は大きく異なるため、最も簡単な方法も最速の方法のようです。興味深いことに、StringBuilderのcharAt()はStringのものよりも少し遅いようです。
ところで、私はCharacterIteratorを使用しないことをお勧めします。「\ uFFFF」文字の乱用は「イテレーションの終わり」であり、本当にひどいハックであると考えるからです。大規模なプロジェクトでは、2つの異なる目的で同じ種類のハッキングを使用する2人の人間が常に存在し、コードが本当に不可解にクラッシュします。
テストの1つを次に示します。
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");
でJavaの8我々はとしてそれを解決することができます。
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
chars()メソッドIntStream
はdocで述べたようにを返します:
このシーケンスからchar値をゼロ拡張するintのストリームを返します。サロゲートコードポイントにマップする文字は、解釈されずに渡されます。ストリームの読み取り中にシーケンスが変更された場合、結果は未定義です。
このメソッドcodePoints()
は、IntStream
ドキュメントごとに次のものも返します。
このシーケンスからコードポイント値のストリームを返します。シーケンスで検出されたサロゲートペアは、Character.toCodePointのように結合され、結果がストリームに渡されます。通常のBMP文字、対になっていないサロゲート、未定義のコード単位を含む他のコード単位は、int値にゼロ拡張されてから、ストリームに渡されます。
charとcode pointはどう違うのですか?この記事で述べたように:
Unicode 3.1は補助文字を追加し、合計文字数を単一の16ビットで区別できる216文字を超えました
char
。したがって、char
値には、Unicodeの基本的な意味単位への1対1のマッピングがありません。JDK 5は、より大きな文字値のセットをサポートするように更新されました。char
タイプの定義を変更する代わりに、新しい補助文字の一部は2つのchar
値のサロゲートペアで表されます。名前の混乱を減らすために、コードポイントを使用して、補助文字を含む特定のUnicode文字を表す番号を参照します。
最後に、なぜforEachOrdered
ありませんかforEach
?
の動作forEach
は明示的に非決定的です。ストリームが定義済みの遭遇順序を持っている場合、はストリームの遭遇順序でforEachOrdered
このストリームの各要素に対してアクションを実行します。したがって、注文が保持されることは保証されません。この質問もチェックしてくださいforEach
で詳細を。
文字、コードポイント、グリフ、および書記素の違いについては、この質問を確認してください。
これにはいくつかの専用クラスがあります:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
char
提供するよりも多くのスペースを必要とするため、文字反復子を使用することがおそらく文字を反復処理するための唯一の正しい方法です。Java char
は16ビットを含み、U + FFFFまでのUnicode文字を保持できますが、UnicodeはU + 10FFFFまでの文字を指定します。16ビットを使用してUnicodeをエンコードすると、可変長文字エンコードになります。このページのほとんどの回答は、Javaエンコーディングが一定長のエンコーディングであると想定していますが、これは誤りです。
クラスパスにGuavaがある場合、次はかなり読みやすい代替です。グアバはこのケースのためにかなり賢明なカスタムリストの実装さえ持っているので、これは非効率であるべきではありません。
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
更新:@Alexが述べたように、Java 8ではCharSequence#chars
使用するものもあります。タイプもIntStreamなので、次のような文字にマップできます。
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
のコードポイントを反復処理する必要がある場合String
(この回答を参照)、CharSequence#codePoints
Java 8で追加されたメソッドを使用することで、より短く/より読みやすい方法になります。
for(int c : string.codePoints().toArray()){
...
}
または、forループの代わりにストリームを直接使用します。
string.codePoints().forEach(c -> ...);
CharSequence#chars
文字のストリームが必要な場合もあります(ただしIntStream
、がないため、ですCharStream
)。
私は使用しません StringTokenizer
レガシーなJDKのクラスの1つである、し。
javadocは言う:
StringTokenizer
新しいコードではその使用は推奨されませんが、互換性の理由で保持されるレガシークラスです。この機能を求める人はString
、java.util.regex
代わりにまたは パッケージのsplitメソッドを使用することをお勧めします。
パフォーマンスが必要な場合は、テストする必要がありますは、環境でます。他に方法はありません。
ここにサンプルコード:
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オンライン私が取得します:
1 10349420
2 526130
3 484200
0
Android x86 API 17では、次のようになります。
1 9122107
2 13486911
3 12700778
0
「Javaチュートリアル:文字列」を参照してください。
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);
}
}
長さを入れてループint len
を使用しfor
ます。
StringTokenizerは、文字列を個々の文字に分解するタスクにはまったく適していません。ではString#split()
、あなたは何も、例えば一致しない正規表現を使って、簡単にそれを行うことができます。
String[] theChars = str.split("|");
ただし、StringTokenizerは正規表現を使用せず、文字間の何にも一致しないように指定できる区切り文字列はありません。同じことを行うために使用できるかわいいハックが 1つあります。文字列自体を区切り文字列として使用し(その中のすべての文字を区切り文字にします)、区切り文字を返します。
StringTokenizer st = new StringTokenizer(str, str, true);
ただし、これらのオプションについては、却下する目的でのみ言及しています。どちらの手法も、元の文字列をcharプリミティブではなく1文字の文字列に分割します。どちらの方法でも、オブジェクトの作成と文字列操作という形でかなりのオーバーヘッドが発生します。これを、実質的にオーバーヘッドのないforループでcharAt()を呼び出すのと比較してください。
上記の回答は、コードポイント値で繰り返されない、ここでの多くのソリューションの問題を指摘しています- サロゲート文字に問題があります。Javaドキュメントもこの問題の概要を示します(「Unicode文字表現」を参照)。とにかく、ここで補足ユニコードセットからいくつかの実際のサロゲート文字を使用するいくつかのコードだと、変換、それらをバック Stringに。.toChars()は文字の配列を返すことに注意してください。サロゲートを処理している場合、2つの文字が必ず必要になります。このコードは、すべての Unicode文字で機能します。
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
このサンプルコードはあなたを助けます!
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);
}
}
したがって、通常、このスレッドですでに複数の人が回答しているjavaの文字列を反復処理するには、2つの方法があります。
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
パフォーマンスが問題になっている場合は、最初の1つを一定の時間で使用することをお勧めします。2つ目がそうでない場合は、Javaの文字列クラスの不変性を考慮して作業を容易にします。