什么是最简单的/更好/最正确的方法来迭代过字符串中Java?
题
StringTokenizer
?转换 String
来一个 char[]
和迭代的吗?别的东西吗?
解决方案
我使用for循环迭代串和使用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之外的字符处理(统一的基本多语种平面),即代码点属于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 +。
我同意StringTokenizer被矫枉过正在这里。其实我试过了上述建议并采取了时间。
我的测试是相当简单:创建一个StringBuilder带约一百万字,转换成一串,并横穿他们每个人与charAt()/后转换为一个char阵/与CharacterIterator一千倍(当然一定要做一些在串这样的编译器不能优化掉整个循环:-)).
结果我的2.6GHz Powerbook(这是一个mac:-))和JAVA1.5:
- 试验1:charAt+串-->3138msec
- 试验2:串转化为列-->9568msec
- 试验3:StringBuilder charAt-->3536msec
- 试验4:CharacterIterator和串-->12151msec
作为结果是显着的不同,最简单的方法也似乎可以最快的一个。有趣的是,charAt()一个StringBuilder似乎是稍微慢于一串。
顺便说一句我建议不要使用CharacterIterator因为我认为它的滥用'\uFFFF'角色作为"结束的迭代"一个真正可怕的黑客。在大型项目,总是有两个人使用同一种破解为两个不同的目的和代码崩溃真的很神秘。
这是一个测试:
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
...
}
如果你有 番石榴 在您的类路径,以下是一个相当可读的替代方案。番石榴甚至有一个相当合理的定义列表执行对于这种情况,所以这个不应该是效率低下。
for(char c : Lists.charactersOf(yourString)) {
// Do whatever you want
}
更新:作为@亚历克斯指出,与Java8还有 CharSequence#chars
使用。即使类型是IntStream,所以它可映射字,如:
yourString.chars()
.mapToObj(c -> Character.valueOf((char) c))
.forEach(c -> System.out.println(c)); // Or whatever you want
在 Java8 我们可以解决这个问题为:
String str = "xyz";
str.chars().forEachOrdered(i -> System.out.print((char)i));
str.codePoints().forEachOrdered(i -> System.out.print((char)i));
该方法chars()返回一个 IntStream
如前所在 doc:
返回的流int零延伸char值从这个 序列。任何char其地图,以代码指的是过去了 通过解释的.如果顺序是突变而流 正在读取,结果是不确定的。
该方法 codePoints()
也返回一个 IntStream
为每doc:
返回的流码点值从这个序列。任何 代理对中遇到的顺序合并为如果通过 符。toCodePoint并将结果传递给流。任何 其他代码单位,包括普通BMP字,未成 代孕母亲和未定义的代码单元,是零扩展到int值 然后传递到流。
怎么是炭和代码点不同? 如前所在 此 文章:
Unicode3.1加入补充字,使总数 字超过216字可以 杰出的通过一个单一的16位
char
.因此,一个char
值没有 再有一个一对一的映射的基本义单元在 Unicode。JAVA5更新,以支持更大的一组字 值。而不是改变定义char
类型,一些 新的补充性人物是由一个代理对 两个char
值。减少的命名混淆,一个代码点将 用于参照的数量表示特定Unicode 字符,包括补充的。
最后为什么 forEachOrdered
而不 forEach
?
该行为 forEach
明确地不确定的在那里 forEachOrdered
执行行动的每个元素这一流的 遇到了流 如果流有一定会遇到的顺序。所以 forEach
并不能保证该命令将被保留。还要检查这个 的问题 更多。
对于 差一字,一个代码点,一字和一个字 检查这个 的问题.
如果您需要通过String
的代码点进行迭代(见本回答)更短的/更可读的方式是使用 CharSequence#codePoints
在Java中加入8 方法:
for(int c : string.codePoints().toArray()){
...
}
或使用流,而不是直接的for循环:
string.codePoints().forEach(c -> ...);
还有 CharSequence#chars
如果希望字符流(虽然它是一个IntStream
,由于不存在CharStream
)。
我不会使用StringTokenizer
,因为它是在这是遗留的JDK类之一。
的Javadoc表示:
String
是传统类 被保留用于兼容性原因 虽然它的使用是在新的鼓励 码。建议人 寻求此功能使用 的java.util.regex
或分割方法 <=>包代替。
如果您需要的性能,那么您必须测试您的环境。没有别的办法。
下面示例代码:
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
在的Android x86的API 17获取:
1 9122107
2 13486911
3 12700778
0
请参阅 Java教程:字符串。
public class StringDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
char[] tempCharArray = new char[len];
char[] charArray = new char[len];
// put original string in an array of chars
for (int i = 0; i < len; i++) {
tempCharArray[i] = palindrome.charAt(i);
}
// reverse array of chars
for (int j = 0; j < len; j++) {
charArray[j] = tempCharArray[len - 1 - j];
}
String reversePalindrome = new String(charArray);
System.out.println(reversePalindrome);
}
}
把长度为int len
并使用for
循环。
的StringTokenizer是完全不适合于打破一个字符串转换为它的各个字符的任务。随着String#split()
您可以通过使用匹配任何一个正则表达式,e.g做到这一点很容易:
String[] theChars = str.split("|");
但StringTokenizer的不使用正则表达式,而且也没有可以指定将匹配字符之间的分隔符无关字符串。这里的是的一个可爱的小黑客,你可以用它来完成同样的事情:使用字符串本身作为分隔符字符串(使得在它的每一个字符分隔符),并使其返回分隔符:
StringTokenizer st = new StringTokenizer(str, str, true);
然而,我只提到这些选项对于驳回他们的目的。这两种技术打破了原有的串入一个字符字符串代替字符元,两者涉及的开销在创建对象和字符串操作的形式很多。与此相比,调用的charAt()在for循环中,这将产生几乎没有开销。
上述答复指出问题的许多解决办法这里不迭代码点值--他们会有麻烦的任何 代理chars.Java文件还概述的问题 在这里, (见"Unicode Character陈述").无论如何,这里的一些代码使用的一些实际的替代文字从补充Unicode设,并将它们转换 回 要一串。注意。toChars()返回一系列的字:如果你正在处理的代理人,你就一定有两个字符。该代码应该作为 任何 Unicode character.
String supplementary = "Some Supplementary: 𠜎𠜱𠝹𠱓";
supplementary.codePoints().forEach(cp ->
System.out.print(new String(Character.toChars(cp))));
此示例代码将帮助你出去!
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Solution {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 10);
map.put("b", 30);
map.put("c", 50);
map.put("d", 40);
map.put("e", 20);
System.out.println(map);
Map sortedMap = sortByValue(map);
System.out.println(sortedMap);
}
public static Map sortByValue(Map unsortedMap) {
Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
sortedMap.putAll(unsortedMap);
return sortedMap;
}
}
class ValueComparator implements Comparator {
Map map;
public ValueComparator(Map map) {
this.map = map;
}
public int compare(Object keyA, Object keyB) {
Comparable valueA = (Comparable) map.get(keyA);
Comparable valueB = (Comparable) map.get(keyB);
return valueB.compareTo(valueA);
}
}