425

Javaで文字列の文字を反復処理するいくつかの方法は次のとおりです。

  1. StringTokenizer?を使用する
  2. をに変換Stringし、char[]それを繰り返します。

反復するための最も簡単/最良/最も正しい方法は何ですか?

4

15 に答える 15

443

forループを使用して文字列を反復処理し、charAt()各文字に文字列を検査させるために使用します。文字列は配列で実装されているため、charAt()メソッドは定数時間操作です。

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

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

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

正しさに関しては、それがここにあるとは思いません。それはすべてあなたの個人的なスタイルに基づいています。

于 2008-10-13T06:13:16.530 に答える
254

2 つのオプション

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

また

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

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

于 2008-10-13T08:06:23.943 に答える
99

Note most of the other techniques described here break down if you're dealing with characters outside of the BMP (Unicode Basic Multilingual Plane), i.e. code points that are outside of the u0000-uFFFF range. This will only happen rarely, since the code points outside this are mostly assigned to dead languages. But there are some useful characters outside this, for example some code points used for mathematical notation, and some used to encode proper names in Chinese.

In that case your code will be:

String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
  int curChar = str.codePointAt(offset);
  offset += Character.charCount(curChar);
  // do something with curChar
}

The Character.charCount(int) method requires Java 5+.

Source: http://mindprod.com/jgloss/codepoint.html

于 2008-12-11T23:04:09.910 に答える
37

Java 8では、次のように解決できます。

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

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

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

このメソッドは、ドキュメントに従ってcodePoints()も返します。IntStream

このシーケンスからコード ポイント値のストリームを返します。シーケンス内で検出されたサロゲート ペアはすべて、Character.toCodePoint によって結合されたかのように結合され、結果がストリームに渡されます。通常の BMP 文字、対になっていないサロゲート、および未定義のコード単位を含むその他のコード単位は、int 値にゼロ拡張され、その後ストリームに渡されます。

文字とコードポイントの違いは? この記事で述べたように:

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

最後に、なぜforEachOrderedではないのforEachですか?

の動作forEachは明示的に非決定論的です。ストリームに検出順序が定義されている場合、 はストリームの検出順序に従ってforEachOrdered、このストリームの各要素に対してアクションを実行します。そのため、注文が維持されることを保証するものではありません。詳細については、この質問も確認してください。forEach

文字、コード ポイント、グリフ、および書記素の違いについては、この質問を確認してください

于 2017-12-10T06:44:28.680 に答える
30

ここで StringTokenizer がやり過ぎであることに同意します。実際、私は上記の提案を試し、時間を費やしました。

私のテストはかなり単純でした: 約 100 万文字の StringBuilder を作成し、それを String に変換し、charAt() で各文字列を走査し、/ char 配列に変換した後、/ CharacterIterator を 1,000 回使用します (もちろん、コンパイラがループ全体を最適化できないように、文字列に対して何かを行います:-))。

私の 2.6 GHz Powerbook (これは Mac です :-) ) と JDK 1.5 での結果:

  • テスト 1: charAt + 文字列 --> 3138msec
  • テスト 2: 文字列を配列に変換 --> 9568msec
  • テスト 3: StringBuilder charAt --> 3536msec
  • テスト 4: CharacterIterator と String --> 12151msec

結果が大きく異なるため、最も簡単な方法が最も速い方法でもあるようです。興味深いことに、StringBuilder の charAt() は、String の charAt() よりもわずかに遅いようです。

ところで、「\uFFFF」文字を「反復の終わり」として悪用するのは本当にひどいハックだと思うので、 CharacterIterator を使用しないことをお勧めします。大規模なプロジェクトでは、同じ種類のハックを 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");
于 2008-12-11T21:08:23.387 に答える
20

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

import java.text.*;

final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
   // process c
   ...
}
于 2008-10-13T06:38:20.033 に答える
19

If you have Guava on your classpath, the following is a pretty readable alternative. Guava even has a fairly sensible custom List implementation for this case, so this shouldn't be inefficient.

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

UPDATE: As @Alex noted, with Java 8 there's also CharSequence#chars to use. Even the type is IntStream, so it can be mapped to chars like:

yourString.chars()
        .mapToObj(c -> Character.valueOf((char) c))
        .forEach(c -> System.out.println(c)); // Or whatever you want
于 2011-03-08T14:30:48.017 に答える
3

StringTokenizerレガシーであるJDKのクラスの1つであるため、使用しません。

javadocによると:

StringTokenizerは、互換性の理由で保持されているレガシークラスですが、新しいコードでは使用しないでください。この機能をお探しの方は、代わりにStringまたは java.util.regexパッケージのsplitメソッドを使用することをお勧めします。

于 2008-10-13T06:26:23.960 に答える
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

于 2008-10-13T06:34:57.700 に答える
0

この回答この回答について詳しく説明します。

上記の回答は、コードポイント値で反復しない多くのソリューションの問題を指摘しています-それらは代理文字に問題があります。Java ドキュメントでも、ここで問題の概要が説明されています(「Unicode 文字表現」を参照)。とにかく、補足の Unicode セットから実際のサロゲート文字を使用し、それらを文字列に変換するコードを次に示します。.toChars() は文字の配列を返すことに注意してください。サロゲートを扱っている場合、必然的に 2 つの文字が必要になります。このコードは、すべてのUnicode 文字で機能するはずです。

    String supplementary = "Some Supplementary: ";
    supplementary.codePoints().forEach(cp -> 
            System.out.print(new String(Character.toChars(cp))));
于 2016-11-05T23:59:27.117 に答える
0

StringTokenizer は、文字列を個々の文字に分割するタスクにはまったく適していません。何String#split()にも一致しない正規表現を使用すると、次のように簡単に実行できます。

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

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

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

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

于 2008-10-13T12:24:48.337 に答える
0

このサンプルコードはあなたを助けます!

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);
    }
}
于 2017-03-15T09:39:00.903 に答える