在Java中迭代字符串字符的方法有:

使用StringTokenizer ? 将String转换为char[]并对其进行迭代。

最简单/最好/最正确的迭代方法是什么?


当前回答

在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:

返回一个int 0的流,从this扩展char值 序列。将传递映射到代理代码点的任何字符 通过粗略的。如果序列发生突变,而流发生突变 被读取时,结果是未定义的。

方法codePoints()也会根据文档返回一个IntStream:

返回此序列中的代码点值流。任何 序列中遇到的代理项对被组合,就像由 的性格。toCodePoint并将结果传递给流。任何 其他代码单位,包括未配对的普通BMP字符 代理和未定义的代码单元从0扩展到int值 然后传递给流。

字符和码点有什么不同?如本文所述:

Unicode 3.1 added supplementary characters, bringing the total number of characters to more than the 2^16 = 65536 characters that can be distinguished by a single 16-bit char. Therefore, a char value no longer has a one-to-one mapping to the fundamental semantic unit in Unicode. JDK 5 was updated to support the larger set of character values. Instead of changing the definition of the char type, some of the new supplementary characters are represented by a surrogate pair of two char values. To reduce naming confusion, a code point will be used to refer to the number that represents a particular Unicode character, including supplementary ones.

最后,为什么forEachOrdered而不是forEach ?

forEach的行为是显式的不确定的,其中forEachOrdered为流的每个元素执行一个操作,如果流具有定义的遇到顺序,则按照流的遇到顺序执行。因此forEach并不保证顺序会被保持。请检查这个问题以了解更多信息。

关于字符、码位、字形和字素之间的区别,请检查这个问题。

其他回答

注意,如果处理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+。

来源:http://mindprod.com/jgloss/codepoint.html

所以通常有两种方法在java中迭代字符串,这已经被很多人在这个线程中回答了,只是添加了我的版本 首先是使用

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 the character array

如果性能受到威胁,那么我会建议在常数时间内使用第一个,如果不是,那么考虑到java中字符串类的不可变性,那么使用第二个会使您的工作更容易。

如果您的类路径中有Guava,下面是一个相当可读的替代方案。对于这种情况,Guava甚至有一个相当合理的自定义List实现,所以这不应该是低效的。

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

更新:正如@Alex指出的那样,在Java 8中也有CharSequence#字符可以使用。甚至它的类型是IntStream,所以它可以映射到像这样的字符:

yourString.chars()
        .mapToObj(c -> Character.valueOf((char) c))
        .forEach(c -> System.out.println(c)); // Or whatever you want

我同意StringTokenizer在这里是多余的。事实上,我尝试了上面的建议,并花了时间。

我的测试相当简单:创建一个带有大约一百万个字符的StringBuilder,将其转换为String,并在转换为char数组/使用CharacterIterator一千次之后使用charAt()遍历每个字符(当然要确保对字符串做一些事情,这样编译器就不能优化掉整个循环:-))。

在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()稍慢。

顺便说一句,我建议不要使用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");

两个选项

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

or

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

第一种可能更快,第二种可能更易读。