在Java中遍历字符串的方法有以下几种:
- 使用
StringTokenizer
? - 将
String
转换为char[]
,然后遍历。
哪种方法是最容易/最好/最正确的遍历方式?
在Java中遍历字符串的方法有以下几种:
StringTokenizer
?String
转换为 char[]
,然后遍历。哪种方法是最容易/最好/最正确的遍历方式?
我使用for循环来迭代字符串,并使用charAt()
方法获取每个字符以便检查。由于String是用数组实现的,因此charAt()
方法是一个常数时间操作。
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
这是我的做法,对我来说最容易。
至于正确性,我认为这里不存在。一切都基于你的个人风格。
String.charAt(int)
的代码只是在执行value[index]
。我认为你把chatAt()
和其他返回码点的函数混淆了。 - antak两个选项
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
或者。for(char c : s.toCharArray()) {
// process c
}
第一种可能更快,而第二种可能更易读。
String str = "....";
int offset = 0, strLen = str.length();
while (offset < strLen) {
int curChar = str.codePointAt(offset);
offset += Character.charCount(curChar);
// do something with curChar
}
< p> Character.charCount(int)
方法需要 Java 5+。
在Java 8中,我们可以这样解决:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
方法chars()返回一个IntStream
,正如文档中所述:
从此序列返回int值的流,用零扩展char值。 任何映射到代理代码点的 char 都会被未经解释地传递。 如果读取流时修改了序列,则结果是未定义的。
方法codePoints()
也根据文档返回一个IntStream
:
从该序列返回代码点值的流。 在序列中遇到的任何代理对都会像通过Character.toCodePoint一样组合,结果会传递给流。 包括普通BMP字符、不成对代理和未定义代码单元在内的任何其他代码单元都将被零扩展为 int 值,然后传递给流。
char和code point有什么区别? 如此文章所述:
Unicode 3.1 添加了补充字符,使得字符总数超过了可以由单个 16 位
char
区分的65536个字符(2^16)。 因此,char
值不再具有与 Unicode 中基本语义单位的一对一映射关系。 JDK 5已更新以支持更大集合的字符值。 该定义并未更改char
类型,而是一些新的补充字符由两个char
值的代理对表示。为了减少名称混淆,代码点将用于引用表示特定Unicode字符的数字,包括补充字符。
最后为什么是forEachOrdered
而不是forEach
?
forEach
的行为明确是非确定性的,而forEachOrdered
为此流的每个元素执行一个操作,如果流具有已定义的遇到顺序,则按照流的遇到顺序执行。 因此,forEach
不能保证保持顺序。 如需更多信息,请参见此问题。
有关字符、代码点、字形和字素之间区别的详细信息,请参见此问题。
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
...
}
char
提供的空间更多。Java的char
包含16位,可以容纳Unicode字符高达U+FFFF,但Unicode规定了字符可达到U+10FFFF。使用16位编码Unicode会导致变长字符编码。本页上的大多数答案都假设Java编码是定长编码,这是错误的。 - cevingfor(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
CharSequence#codePoints
方法:for(int c : string.codePoints().toArray()){
...
}
或者直接使用流而不是for循环:
string.codePoints().forEach(c -> ...);
如果您想要一个字符流,也可以使用CharSequence#chars
(尽管它是一个IntStream
,因为没有CharStream
)。
如果您需要性能,那么必须在您的环境上进行测试。别无选择。
这里是示例代码:
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
1 9122107
2 13486911
3 12700778
0
我不建议使用StringTokenizer
,因为它是JDK中的遗留类之一。
javadoc说:
StringTokenizer
是一个保留的遗留类,用于兼容性考虑,尽管在新代码中不鼓励使用。建议任何需要此功能的人改用String
的split方法或java.util.regex
包。