如何将for-each循环应用于字符串中的每个字符?

253

我想要在字符串中迭代每个字符。

所以我考虑:

for (char c : "xyz")

但是我遇到了编译器错误:

MyClass.java:20: foreach not applicable to expression type

我该如何做到这一点?

9个回答

440
使用toCharArray()是遍历String中每个char最简单的方法:
for (char ch: "xyz".toCharArray()) {
}

这使得您可以使用for-each结构的简洁性,但不幸的是,String(不可变)必须执行防御性副本以生成char[](可变),因此存在一些成本惩罚。
文档中:

[toCharArray()返回]一个新分配的字符数组,其长度为此字符串的长度,并且其内容已初始化为包含由该字符串表示的字符序列。

有更冗长的方法来迭代数组中的字符(常规for循环,CharacterIterator等),但如果您愿意支付toCharArray()的成本,则for-each是最简洁的。

2
编译器会认识到不需要真正的副本并应用适当的优化吗? - Pacerier
1
@Pacerier 不,目前的Java编译器永远不会优化代码。 - randers
1
@RAnders00 所以,在这种情况下 toCharArray( ) 被调用了3次? - denvercoder9
3
使用字符串长度为 211900000 时,使用 for (char c : str.toCharArray()) { } 完成所需时间约为 250 毫秒,而使用 for (char c : charArray) { } 仅需要不到 10 毫秒的时间。当然,这些时间值将受硬件影响,但重要的是其中一种方法比另一种显著更费时。 - denvercoder9
7
不,toCharArray() 不在循环体内;它只被调用一次,然后循环遍历该结果数组。(增强型 for 循环与通用的 for 循环不同,后者会在每次迭代时评估停止条件。) - not-just-yeti
显示剩余2条评论

66
String s = "xyz";
for(int i = 0; i < s.length(); i++)
{
   char c = s.charAt(i);
}

 


12
OP 希望使用增强型 for 循环。 - AmitG
5
因为这与原帖作者要求的完全不同,所以被投了反对票。虽然这是一个有效的解决方案,但它并不符合问题的要求。 - Allison
4
不管原作者的预期如何,我想这比使用for-each要性能更好。 - Eric

13
如果您使用Java 8,可以在字符串上使用chars()来获取字符流,但是您需要将int强制转换回char,因为chars()返回一个IntStream
"xyz".chars().forEach(i -> System.out.print((char)i));
如果您使用带有Eclipse Collections的Java 8,则可以使用CharAdapterforEach方法和lambda表达式或方法引用迭代String中的所有字符。
Strings.asChars("xyz").forEach(c -> System.out.print(c));

这个特定的例子也可以使用方法引用。

Strings.asChars("xyz").forEach(System.out::print)

注意:我是 Eclipse Collections 的提交者。


12

另一个有用的解决方案,您可以将该字符串作为字符串数组进行处理。

for (String s : "xyz".split("")) {
    System.out.println(s);
}

5
优雅,但不幸的是,如果split()操作中字符串的任何元素映射到多个Java字符,这将无法工作,这几乎适用于所有表情符号。例如,您示例的这种变体将循环四次而不是三次:for (String s : "xz".split("")) { System.out.println(s); } - skomisa

8
你需要使用 String 类的 toCharArray() 方法将 String 对象转换为 char 数组:
String str = "xyz";
char arr[] = str.toCharArray(); // convert the String object to array of char

// iterate over the array using the for-each loop.       
for(char c: arr){
    System.out.println(c);
}

7
在Java 8中,我们可以这样解决:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));    

方法chars()返回一个IntStream,如doc所述:
返回一个从此序列中零扩展字符值的int流。任何映射到代理代码点的字符都将通过未解释的方式传递。如果在读取流时对序列进行了更改,则结果是未定义的。
为什么使用forEachOrdered而不是forEach?
forEach的行为是明确不确定的,而forEachOrdered针对该流的每个元素执行一个操作,在流的遇到顺序中(如果流具有定义的遇到顺序)遇到的顺序中执行。因此,forEach不能保证顺序会被保留。另请参见question以获取更多信息。
我们也可以使用 codePoints()来打印输出,有关更多详细信息,请参见答案

这将创建多个对象,因此此答案没有完整的内容,除非提醒说,这可能不如简单的索引循环高效。 - toolforger

5

不幸的是,Java没有让String实现Iterable<Character>。这可以很容易地完成。有StringCharacterIterator,但它甚至没有实现Iterator...所以你需要自己创建一个:

public class CharSequenceCharacterIterable implements Iterable<Character> {
    private CharSequence cs;

    public CharSequenceCharacterIterable(CharSequence cs) {
        this.cs = cs;
    }

    @Override
    public Iterator<Character> iterator() {
        return new Iterator<Character>() {
            private int index = 0;

            @Override
            public boolean hasNext() {
                return index < cs.length();
            }

            @Override
            public Character next() {
                return cs.charAt(index++);
            }
        };
    }
}

现在,您可以(在某种程度上)轻松地运行for (char c : new CharSequenceCharacterIterable("xyz"))……


3
在这种情况下,您还可以使用lambda表达式。
    String s = "xyz";
    IntStream.range(0, s.length()).forEach(i -> {
        char c = s.charAt(i);
    });

-8

对于遍历字符串,您也可以使用字符串的charAt()方法。

例如:

String str = "xyz"; // given String
char st = str.charAt(0); // for example we take 0 index element 
System.out.println(st); // print the char at 0 index 

charAt() 是 Java 中字符串处理的一个方法,它可以帮助遍历字符串并查找特定字符。


2
你知道 for (char st: "xyz".toCharArray()) {} 是什么吗? - AmitG
-1 不相关(主题:for-each),使用charAt代替charAT。 - peenut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接