StringTokenizer
?转换String
为a char[]
并迭代它?别的什么?
我使用for循环来迭代字符串并charAt()
用来让每个字符检查它.由于String是使用数组实现的,因此该charAt()
方法是一个常量时间操作.
String s = "...stuff..."; for (int i = 0; i < s.length(); i++){ char c = s.charAt(i); //Process char }
这就是我要做的.这对我来说似乎最简单.
就正确性而言,我不相信存在于此.这完全取决于您的个人风格.
两种选择
for(int i = 0, n = s.length() ; i < n ; i++) { char c = s.charAt(i); }
要么
for(char c : s.toCharArray()) { // process c }
第一个可能更快,然后第二个可能更可读.
注意,如果您处理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
我同意StringTokenizer在这里有点过分.实际上我尝试了上面的建议,并花时间.
我的测试非常简单:创建一个大约有一百万个字符的StringBuilder,将它转换为一个String,然后使用charIt()/在转换为char数组/使用CharacterIterator一千次后遍历每个字符串(当然要确保在字符串上执行某些操作,以便编译器无法优化整个循环:-)).
我的2.6 GHz Powerbook(这是一个mac :-))和JDK 1.5的结果:
测试1:charAt + String - > 3138毫秒
测试2:字符串转换为数组 - > 9568毫秒
测试3:StringBuilder charAt - > 3536毫秒
测试4:CharacterIterator和String - > 12151毫秒
由于结果显着不同,最直接的方式似乎也是最快的方式.有趣的是,StringBuilder的charAt()似乎比String更慢.
BTW我建议不要使用CharacterIterator,因为我认为它滥用'\ uFFFF'字符作为"迭代结束"是一个非常糟糕的黑客.在大项目中,总有两个人使用相同类型的黑客用于两个不同的目的,代码崩溃真的很神秘.
这是其中一项测试:
int count = 1000; ... System.out.println("Test 1: charAt + String"); long t = System.currentTimeMillis(); int sum=0; for (int i=0; i
5> Bruno De Fra..:有一些专门的类:
import java.text.*; final CharacterIterator it = new StringCharacterIterator(s); for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) { // process c ... }
对于像迭代不可变char数组这样简单的东西看起来像是一种过度杀伤.
使用字符迭代器可能是迭代字符的唯一正确方法,因为Unicode需要比Java`char`提供的空间更多的空间.Java`char`包含16位,可以在U + FFFF上保存Unicode字符,但Unicode指定U + 10FFFF以上的字符.使用16位对Unicode进行编码会产生可变长度的字符编码.本页的大多数答案都假设Java编码是一种常量长度编码,这是错误的.
@ceving看起来字符迭代器似乎不会帮助你使用非BMP字符:http://www.oracle.com/us/technologies/java/supplementary-142654.html
同意@ddimitrov - 这太过分了.使用迭代器的唯一原因是利用foreach,这比for循环更容易"看".如果你打算写一个传统的for循环,那么不妨使用charAt()
6> Touko..:如果你的类路径上有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
7> i_am_zero..:在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的流,从此序列中对char值进行零扩展.任何映射到代理代码点的char都会被解释.如果在读取流时序列被突变,则结果是未定义的.
该方法
codePoints()
还IntStream
根据doc 返回:返回此序列中的代码点值流.序列中遇到的任何代理对都被组合,就好像通过Character.toCodePoint一样,结果传递给流.任何其他代码单元(包括普通BMP字符,未配对代理和未定义代码单元)都将零扩展为int值,然后传递给流.
char和代码如何区别?正如提到的这个文章:
Unicode 3.1添加了补充字符,使总字符数超过216个字符,可以通过单个16位进行区分
char
.因此,char
值不再与Unicode中的基本语义单元进行一对一映射.更新了JDK 5以支持更大的字符值集.char
一些新的补充字符不是改变类型的定义,而是由两个char
值的代理对表示.为了减少命名混淆,将使用代码点来指代表示特定Unicode字符的数字,包括补充字符.最后为什么
forEachOrdered
不forEach
呢?如果流具有已定义的遭遇顺序,则在流的遭遇顺序中执行针对此流的每个元素的操作时,行为
forEach
明确是非确定性的.所以不保证订单会被保留.另请查看此问题.forEachOrdered
forEach
对于字符,代码点,字形和字形之间的差异,请检查此问题.
8> Alex..:如果你需要遍历a的代码点
String
(参见这个答案),更简单/更易读的方法是使用CharSequence#codePoints
Java 8中添加的方法:for(int c : string.codePoints().toArray()){ ... }或直接使用流而不是for循环:
string.codePoints().forEach(c -> ...);还有
CharSequence#chars
,如果你想要的字符流(虽然它是IntStream
,因为没有CharStream
).