当前位置:  开发笔记 > 编程语言 > 正文

在Java中迭代字符串字符的最简单/最好/最正确的方法是什么?

如何解决《在Java中迭代字符串字符的最简单/最好/最正确的方法是什么?》经验,为你挑选了8个好方法。

StringTokenizer?转换String为a char[]并迭代它?别的什么?



1> jjnguy..:

我使用for循环来迭代字符串并charAt()用来让每个字符检查它.由于String是使用数组实现的,因此该charAt()方法是一个常量时间操作.

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

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

这就是我要做的.这对我来说似乎最简单.

就正确性而言,我不相信存在于此.这完全取决于您的个人风格.


混乱代码以获得*微​​小*性能增益.在您确定此代码区域对速度至关重要之前,请避免这种情况.
请注意,此技术为您提供*字符*,而不是*代码点*,这意味着您可能会获得代理.
它可能内联length(),即提升调用几帧的方法,但它更有效地执行此操作(int i = 0,n = s.length(); i 编译器是否内联了length()方法?
@ikh*charAt不是O(1)*:怎么会这样?`String.charAt(int)`的代码只是做`value [index]`.我认为你把`chatAt()`与其他能给你代码点的东西混淆了.

2> Dave Cheney..:

两种选择

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

要么

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

第一个可能更快,然后第二个可能更可读.


我认为编译器优化会为您解决这个问题.
加一个用于将s.length()放在初始化表达式中.如果有人不知道为什么,那是因为只有当它作为i @Matthias您可以使用Javap类反汇编程序来查看是否确实避免了对for循环终止表达式中s.length()的重复调用.请注意,在发布的代码OP中,对s.length()的调用是在初始化表达式中,因此语言语义已经保证它只会被调用一次.
@prasopes注意,大多数java优化都发生在运行时,而不是在类文件中.即使您看到对length()的重复调用也不一定表示运行时惩罚.
@Lasse,假定的原因是为了提高效率-您的版本在每次迭代时都调用length()方法,而Dave在初始化程序中调用一次。就是说,极有可能JIT(“及时”)优化器将优化多余的调用,因此它可能只是可读性差异,而没有实际收益。

3> sk...:

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


因为这似乎是唯一对BMP之外的Unicode字符正确的答案
您可以使用int来存储整个代码点,否则每个char只会存储定义代码点的两个代理对中的一个.

4> 小智..:

我同意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字符的数字,包括补充字符.

最后为什么forEachOrderedforEach呢?

如果流具有已定义的遭遇顺序,则在流的遭遇顺序中执行针对此流的每个元素的操作时,行为forEach明确是非确定性的.所以不保证订单会被保留.另请查看此问题.forEachOrderedforEach

对于字符,代码点,字形和字形之间的差异,请检查此问题.



8> Alex..:

如果你需要遍历a的代码点String(参见这个答案),更简单/更易读的方法是使用CharSequence#codePointsJava 8中添加的方法:

for(int c : string.codePoints().toArray()){
    ...
}

或直接使用流而不是for循环:

string.codePoints().forEach(c -> ...);

还有CharSequence#chars,如果你想要的字符流(虽然它是IntStream,因为没有CharStream).

推荐阅读
殉情放开那只小兔子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有