web-dev-qa-db-ja.com

Javaで文字列の文字を反復処理するための最も簡単な/最善の/最も正しい方法は何ですか?

StringTokenizerStringchar[]に変換して、それを繰り返しますか?他に何か?

277
Paul Wicks

Forループを使用して文字列を繰り返し、charAt()を使用して各文字を調べます。 Stringは配列を使って実装されているので、charAt()メソッドは一定時間操作です。

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

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

それが私がやることです。それは私にとって最も簡単なようです。

正確さに関する限り、私はそれがここに存在するとは思わない。それはすべてあなたの個人的なスタイルに基づいています。

300
jjnguy

2つの選択肢

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

または

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

最初のほうがおそらく速く、次に2番目の方が読みやすいでしょう。

179
Dave Cheney

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+を必要とします。

出典: http://mindprod.com/jgloss/codepoint.html

88
sk.

これにはいくつかの専用クラスがあります。

import Java.text.*;

final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
   // process c
   ...
}
19
Bruno De Fraine

クラスパスに Guava がある場合は、次の方法で読みやすくなります。このケースでは、Guavaにはかなり賢明なカスタムList実装さえあるので、これは非効率的ではないはずです。

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
17
Touko

Stringのコードポイントを反復処理する必要がある場合(これ の回答 を参照)には、 CharSequence#codePoints メソッドを追加する方法があります。 Java 8では:

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

またはforループの代わりに直接ストリームを使用します。

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

文字のストリームが必要な場合は CharSequence#chars もあります(ただしIntStreamはないのでCharStreamです)。

12
Alex

Java 8ではそれを次のように解くことができます。

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

メソッドchars()は doc で述べたようにIntStreamを返します。

このシーケンスのchar値をゼロ拡張したintのストリームを返します。サロゲートコードポイントにマップされるすべてのcharは、未解釈のまま渡されます。ストリームの読み取り中にシーケンスが変更された場合、結果は未定義です。

メソッドcodePoints()はdocに従ってIntStreamも返します。

このシーケンスからコードポイント値のストリームを返します。シーケンス内で見つかったサロゲートペアは、Character.toCodePointによる場合と同様に組み合わされ、結果がストリームに渡されます。通常のBMP文字、対になっていないサロゲート、未定義のコード単位など、その他のコード単位は、int値にゼロ拡張されてからストリームに渡されます。

charとcode pointはどう違うのですか?this articleで述べたように:

Unicode 3.1では補助文字が追加され、合計16文字のcharで区別できる合計文字数が216文字を超えました。したがって、char値は、Unicodeの基本的な意味単位への1対1のマッピングを持たなくなりました。 JDK 5は、より大きな文字値のセットをサポートするように更新されました。 char型の定義を変更する代わりに、新しい補助文字のいくつかは2つのchar値のサロゲートペアによって表されます。命名の混乱を少なくするために、コードポイントを使用して、補足のものも含め、特定のUnicode文字を表す番号を参照します。

最後になぜforEachOrderedではなくforEachですか?

forEachの振る舞いは明示的に非決定的です。ここでforEachOrderedはこのストリームの各要素に対してアクションを実行します。ストリームに定義済みのエンカウンター順序がある場合はストリームのエンカウンター順序で実行されます。そのためforEachは順序が守られることを保証しません。また、この question もチェックしてください。

文字、コードポイント、グリフ、書記素の違い _については、この question を確認してください。

12
i_am_zero

StringTokenizerはJDKのレガシークラスの1つなので、使用しません。

Javadocは言います:

StringTokenizerは互換性の理由から保持されているレガシークラスですが、新しいコードでは使用をお勧めしません。この機能を探している人は誰でも代わりにStringのsplitメソッドかJava.util.regexパッケージを使うことをお勧めします。

3
Alan

StringTokenizerは、文字列を個々の文字に分割するというタスクにはまったく適していません。 String#split()では、何にもマッチしない正規表現を使うことで簡単にそれを行うことができます。

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

しかし、StringTokenizerは正規表現を使用していません。また、指定できる区切り文字列が文字間の何にも一致しないということはありません。そこにがあります同じことを達成するために使用できる1つのかわいいハック。そしてそれに区切り文字を返させる:

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

しかし、私はそれらを却下する目的のためにこれらのオプションに言及するだけです。どちらの手法も元の文字列をcharプリミティブの代わりに1文字の文字列に分割します。どちらもオブジェクト作成と文字列操作の形で大量のオーバーヘッドを伴います。 forループでcharAt()を呼び出すのと比較すると、事実上オーバーヘッドは発生しません。

0
Alan Moore

パフォーマンスが必要な場合は、あなたがあなたの環境でテストしなければなりません。他に方法はありません。

ここでのコード例:

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
0
Enyby

このサンプルコードはあなたを助けるでしょう!

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);
    }
}
0
devDeejay

this answer および this answer について詳しく説明します。

上記の答えは、コードポイント値で反復しない解決策の多くの問題を指摘しています - 代理文字 では問題があるでしょう。 Javaのドキュメントでは here の問題についても概説しています(「Unicode文字表現」を参照)。とにかく、ここにいくつかの実際のサロゲート文字を補足Unicodeセットから使用し、それらをbackをStringに変換するコードがあります。 .toChars()は文字の配列を返すことに注意してください。もしあなたがサロゲートを扱っているなら、あなたは必然的に2文字を持つでしょう。このコードはany Unicode文字に対して機能するはずです。

    String supplementary = "Some Supplementary: ????????????????";
    supplementary.codePoints().forEach(cp -> 
            System.out.print(new String(Character.toChars(cp))));
0
Hawkeye Parker

The Java Tutorials:String を参照してください。

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ループを使用してください。

0
Eugene Yokota