何が最も簡単な/最高/最も正しい方に対して繰り返し処理を実行する文字の文字列をJava?
質問
StringTokenizer
?に変換し String
へ char[]
とに対して繰り返し処理を実行す。何か?
解決
forループを使用して文字列を反復処理し、 charAt()
を使用して各文字を取得して調べます。文字列は配列で実装されるため、 charAt()
メソッドは一定時間の操作です。
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
それが私がすることです。一番簡単だと思う。
正確性に関する限り、それがここにあるとは思わない。それはすべてあなたの個人的なスタイルに基づいています。
他のヒント
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 基本的な多言語面)、つまり、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は失わせないアイテムです。実際に私の提案は、上した。
私が試したんですがシンプルです:をStringBuilder約百万文字に変換する文字列を、切り込みと送りを小さくひとりがしっかりとcharAt()以降に変換するchar配列とれたCharacterIterator千倍に(もちろんだんに文字列のコンパイラでの最適化の全体をループ:-)).
結果は私の2.6GHz Powerbook(mac:-)のJDK1.5:
- 試験1:charAt+String-->3138msec
- 試験2:文字列に変換され配列-->9568msec
- 試験3:StringBuilder charAt-->3536msec
- 試験4:れたCharacterIteratorの文字列-->12151msec
その結果とは大きな違いがあり、最も簡単な方法はもう最速です。興味深いことに、charAt()のStringBuilderがやや遅の文字列になります。
ちなみに私は利用しないCharacterIteratorとしての私の考えでは、その濫用の"\uFFFF'の文字として"終了の繰り返し"は、かhack.ビックプロジェクトがずっと同じようなハッキングのための二つの異なる目的のコードクラッシュ本当に不思議.
ここでの試験:
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");
これ専用のクラスがいくつかあります:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
クラスパスに 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
Java 8 では、次のように解決できます。
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
doc :
これからchar値をゼロ拡張するintのストリームを返します シーケンス。サロゲートコードポイントにマップする任意のcharが渡されます 通訳なし。ストリームが変更されている間にシーケンスが変更された場合 読み込まれている場合、結果は未定義です。
codePoints()
メソッドは、ドキュメントに従って IntStream
も返します:
このシーケンスからコードポイント値のストリームを返します。どれか シーケンスで検出されたサロゲートペアは、次のように結合されます。 Character.toCodePointと結果がストリームに渡されます。どれか ペアになっていない通常のBMP文字を含む他のコード単位 サロゲートおよび未定義のコード単位は、int値にゼロ拡張されます ストリームに渡されます。
charとコードポイントの違い こちらの記事:
Unicode 3.1は補助文字を追加し、合計数をもたらしました 216文字を超える文字 単一の16ビットの
char
で区別されます。したがって、char
値は より長く、基本的なセマンティックユニットへの1対1のマッピングがあります。 Unicode。 JDK 5は、より大きな文字セットをサポートするように更新されました 値。char
タイプの定義を変更する代わりに、 新しい補助文字はサロゲートペアで表されます 2つのchar
値。命名の混乱を減らすために、コードポイントは 特定のUnicodeを表す番号を参照するために使用 補助的なものを含むキャラクター。
最後に forEachOrdered
であり、 forEach
ではない理由
forEach
の動作は明示的に非決定的であり、 forEachOrdered
はこのストリームの各要素に対して、ストリームの遭遇順序でアクションを実行します strong>ストリームに遭遇順序が定義されている場合。したがって、 forEach
は、順序が維持されることを保証しません。また、この質問も確認してください。
文字、コードポイント、グリフ、書記素の違いについては、これを確認してください
またはforループの代わりに直接ストリームを使用する: String
のコードポイントを反復処理する必要がある場合(回答をご覧ください) )より短く/読みやすい方法は、 CharSequence#codePoints
メソッド:for(int c : string.codePoints().toArray()){
...
}
string.codePoints().forEach(c -> ...);
もあります。 CharSequence#chars
文字のストリームが必要な場合( CharStream
がないため IntStream
ですが)。
StringTokenizer
は、古いJDKのクラスの1つであるため、使用しません。
javadocのコメント:
StringTokenizer
は、 互換性の理由で保持されます 新しい使用は推奨されていませんが コード。誰でもお勧めです この機能を探すにはString
のsplitメソッドまたは 代わりにjava.util.regex
パッケージ。
パフォーマンスが必要な場合は、環境でテストする必要があります。他の方法はありません。
サンプルコード:
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文字表現」を参照)。とにかく、補助Unicodeセットの実際のサロゲート文字を使用し、それらを back を文字列に変換するコードがあります。 .toChars()は文字の配列を返すことに注意してください。サロゲートを処理する場合、必然的に2つの文字が必要になります。このコードは any 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);
}
}