StringTokenizer
?将String
转换为char[]
并对其进行迭代?还有别的吗?
答案 0 :(得分:295)
我使用for循环迭代字符串并使用charAt()
让每个字符检查它。由于String是使用数组实现的,因此charAt()
方法是一个常量时间操作。
String s = "...stuff...";
for (int i = 0; i < s.length(); i++){
char c = s.charAt(i);
//Process char
}
这就是我要做的。这对我来说似乎最容易。
就正确性而言,我不相信存在于此。这完全取决于你的个人风格。
答案 1 :(得分:178)
两个选项
for(int i = 0, n = s.length() ; i < n ; i++) {
char c = s.charAt(i);
}
或
for(char c : s.toCharArray()) {
// process c
}
第一个可能更快,然后第二个可能更具可读性。
答案 2 :(得分:88)
注意,如果您处理的是BMP之外的字符(Unicode Basic Multilingual Plane),即超出u0000-uFFFF范围的code points,则此处描述的大多数其他技术都会被破坏。这种情况很少发生,因为此外的代码点主要分配给死语言。但是除此之外还有一些有用的字符,例如用于数学符号的一些代码点,还有一些用于用中文编码专有名称。
在这种情况下,您的代码将是:
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 +。
答案 3 :(得分:24)
我同意StringTokenizer在这里有点过分。实际上我尝试了上面的建议,并花时间。
我的测试非常简单:创建一个大约有一百万个字符的StringBuilder,将它转换为一个String,然后使用charIt()/在转换为char数组/使用CharacterIterator一千遍后遍历每个字符串(当然确保对字符串执行某些操作,以便编译器无法优化整个循环:-))。
我的2.6 GHz Powerbook(这是一个mac :-))和JDK 1.5的结果:
由于结果显着不同,最直接的方式似乎也是最快的方式。有趣的是,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<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");
答案 4 :(得分:19)
有一些专门的课程:
import java.text.*;
final CharacterIterator it = new StringCharacterIterator(s);
for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
// process c
...
}
答案 5 :(得分:17)
如果您的类路径中有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
答案 6 :(得分:12)
如果您需要遍历String
的代码点(请参阅此answer),则使用Java 8中添加的CharSequence#codePoints
方法可以使用更短/更易读的方法: / p>
for(int c : string.codePoints().toArray()){
...
}
或直接使用流而不是for循环:
string.codePoints().forEach(c -> ...);
如果你想要一个角色流,还有CharSequence#chars
(虽然它是IntStream
,因为没有CharStream
)。
答案 7 :(得分:11)
在 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 通过解释。如果序列在流时发生变异 正在阅读,结果未定义。
方法codePoints()
也会根据doc:
IntStream
从此序列返回代码点值流。任何 在序列中遇到的代理对被组合起来,如同 Character.toCodePoint和结果传递给流。任何 其他代码单元,包括普通的BMP字符,不成对 代理和未定义的代码单元被零扩展为int值 然后传递给流。
字符和代码有何不同?正如this文章中所述:
Unicode 3.1添加了补充字符,带来了总数 字符超过216个字符即可 由一个16位
char
区分。因此,char
值为 更长的是与基本语义单元的一对一映射 Unicode格式。 JDK 5已更新,以支持更大的字符集 值。一些,而不是改变char
类型的定义 新的补充字符由代理对表示 两个char
值。为了减少命名混淆,代码点将是 用于表示代表特定Unicode的数字 性格,包括补充性质。
最后为什么forEachOrdered
而不是forEach
?
forEach
的行为明确是不确定的,因为forEachOrdered
对此流的每个元素执行操作,如果流具有遇到流的顺序定义的遭遇订单。所以forEach
并不保证订单会被保留。另请查看question以获取更多信息。
对于字符,代码点,字形和字素之间的差异,请检查此question。
答案 8 :(得分:3)
我不会使用StringTokenizer
,因为它是JDK遗留下来的类之一。
javadoc说:
StringTokenizer
是一个遗留类 因兼容性原因而保留 虽然在新的情况下不鼓励使用它 码。任何人都可以推荐 寻求此功能使用String
的分割方法 而是java.util.regex
包。
答案 9 :(得分:0)
请参阅The Java Tutorials: Strings。
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
循环。
答案 10 :(得分:0)
StringTokenizer完全不适合将字符串分解为单个字符的任务。使用String#split()
,您可以使用不匹配的正则表达式轻松完成此操作,例如:
String[] theChars = str.split("|");
但是StringTokenizer不使用正则表达式,并且没有可以指定的分隔符字符串,它将匹配字符之间的任何内容。 是一个可爱的小黑客你可以用来完成同样的事情:使用字符串本身作为分隔符字符串(使其中的每个字符都是分隔符)并让它返回分隔符:
StringTokenizer st = new StringTokenizer(str, str, true);
但是,我只是提到这些选项以解雇它们。这两种技术都将原始字符串分解为单字符字符串而不是char原语,并且两者都以对象创建和字符串操作的形式涉及大量开销。将其与for循环中的charAt()进行比较,几乎不会产生任何开销。
答案 11 :(得分:0)
以上答案指出了许多解决方案的问题,这些解决方案不会按代码点值进行迭代 - 它们会遇到任何问题surrogate chars。 java文档还概述了问题here(参见&#34; Unicode字符表示和#34;)。无论如何,这里有一些代码使用来自补充Unicode集的一些实际代理字符,并将它们 back 转换为字符串。请注意.toChars()返回一个chars数组:如果你正在处理代理,你必须有两个字符。此代码适用于任何 Unicode字符。
npm http GET https://registry.npmjs.org/onoff
npm ERR! Error: failed to fetch from registry: onoff
npm ERR! at /usr/share/npm/lib/utils/npm-registry-client/get.js:139:12
npm ERR! at cb (/usr/share/npm/lib/utils/npm-registry-client/request.js:31:9)
npm ERR! at Request._callback (/usr/share/npm/lib/utils/npm-registry-client/request.js:136:18)
npm ERR! at Request.callback (/usr/lib/nodejs/request/main.js:119:22)
npm ERR! at Request.<anonymous> (/usr/lib/nodejs/request/main.js:212:58)
npm ERR! at Request.emit (events.js:88:20)
npm ERR! at ClientRequest.<anonymous> (/usr/lib/nodejs/request/main.js:412:12)
npm ERR! at ClientRequest.g (events.js:156:14)
npm ERR! at ClientRequest.emit (events.js:67:17)
npm ERR! at HTTPParser.parserOnIncomingClient [as onIncoming] (http.js:1256:7)
npm ERR! You may report this log at:
npm ERR! <http://bugs.debian.org/npm>
npm ERR! or use
npm ERR! reportbug --attach /home/pi/homeautomation/npm-debug.log npm
npm ERR!
npm ERR! System Linux 4.1.19+
npm ERR! command "/usr/bin/nodejs" "/usr/bin/npm" "install" "onoff" "--save"
npm ERR! cwd /home/pi/homeautomation
npm ERR! node -v v0.6.19
npm ERR! npm -v 1.1.4
npm ERR! message failed to fetch from registry: onoff
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR! /home/pi/homeautomation/npm-debug.log
npm not ok
答案 12 :(得分:0)
此示例代码将帮助您!
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);
}
}
答案 13 :(得分:0)
如果需要性能,则必须必须进行测试。没有其他方法。
以下示例代码:
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 online上,我得到:
1 10349420
2 526130
3 484200
0
在Android x86 API 17上,我得到:
1 9122107
2 13486911
3 12700778
0
答案 14 :(得分:0)
因此,通常有两种方法可以遍历java中的字符串,该线程已经在这里被多人回答,只需添加我的版本即可 首先是使用
String s = sc.next() // assuming scanner class is defined above
for(int i=0; i<s.length; i++){
s.charAt(i) // This being the first way and is a constant time operation will hardly add any overhead
}
char[] str = new char[10];
str = s.toCharArray() // this is another way of doing so and it takes O(n) amount of time for copying contents from your string class to character array
如果性能受到威胁,那么我建议您恒定时间使用第一个,如果不这样做,那么考虑到Java中字符串类的不变性,第二个就可以简化您的工作。